From d2f988d545530f769c25d657b576f1e9938c1729 Mon Sep 17 00:00:00 2001 From: vitalets <noginsk@rambler.ru> Date: Sun, 25 Nov 2012 12:12:26 +0400 Subject: [PATCH 01/24] list input + checklist alpha --- CHANGELOG.txt | 6 +- src/editable-form/editable-form.js | 15 +- src/inputs/checklist.js | 150 +++++++++++++++++ src/inputs/date/date.js | 1 + src/inputs/dateui/dateui.js | 1 + src/inputs/list.js | 255 +++++++++++++++++++++++++++++ src/inputs/select.js | 236 ++------------------------ src/inputs/text.js | 1 + src/inputs/textarea.js | 1 + test/loader.js | 4 +- 10 files changed, 444 insertions(+), 226 deletions(-) create mode 100644 src/inputs/checklist.js create mode 100644 src/inputs/list.js diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 90c417d..da494b4 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,6 +1,10 @@ X-editable changelog ============================= - + +Version 1.0.2 wip +---------------------------- +[enh] updated docs: inputs dropdown menu, global templates section (vitalets) + Version 1.0.1 Nov 22, 2012 ---------------------------- diff --git a/src/editable-form/editable-form.js b/src/editable-form/editable-form.js index 8a975b0..d93042a 100644 --- a/src/editable-form/editable-form.js +++ b/src/editable-form/editable-form.js @@ -17,7 +17,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. EditableForm.prototype = { constructor: EditableForm, - initInput: function() { + initInput: function() { //called once var TypeConstructor, typeOptions; //create input of specified type @@ -247,7 +247,18 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. option: function(key, value) { this.options[key] = value; - } + if(key === 'value') { + this.setValue(value); + } + }, + + setValue: function(value, convertStr) { + if(convertStr) { + this.value = this.input.str2value(value); + } else { + this.value = value; + } + } }; /* diff --git a/src/inputs/checklist.js b/src/inputs/checklist.js new file mode 100644 index 0000000..158d509 --- /dev/null +++ b/src/inputs/checklist.js @@ -0,0 +1,150 @@ +/** +Checklist input. Internally value stored as array. + +@class checklist +@extends list +@final +@example +<a href="#" id="status" data-type="checklist" data-pk="1" data-url="/post" data-original-title="Select options"></a> +<script> +$(function(){ + $('#options').editable({ + value: [2, 3], + source: [ + {value: 1, text: 'Active'}, + {value: 2, text: 'Blocked'}, + {value: 3, text: 'Deleted'} + ] + } + }); +}); +</script> +**/ +(function ($) { + + var Checklist = function (options) { + this.init('checklist', options, Checklist.defaults); + }; + + $.fn.editableform.utils.inherit(Checklist, $.fn.editableform.types.list); + + $.extend(Checklist.prototype, { + renderList: function() { + var $label, $div; + if(!$.isArray(this.sourceData)) { + return; + } + + for(var i=0; i<this.sourceData.length; i++) { + $label = $('<label>').text(' '+this.sourceData[i].text) + .prepend($('<input>', { + type: 'checkbox', + value: this.sourceData[i].value, + name: this.options.name + })); + + $('<div>').append($label).appendTo(this.$input); + } + }, + + value2str: function(value) { + return $.isArray(value) ? value.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; + } + return value; + }, + + //set checked on required checkboxes + value2input: function(value) { + var $checks = this.$input.find('input[type="checkbox"]'); + $checks.removeAttr('checked'); + if($.isArray(value) && value.length) { + $checks.each(function(i, el) { + if($.inArray($(el).val(), value) !== -1) { + $(el).attr('checked', 'checked'); + } + }); + } + }, + + input2value: function() { + var checked = []; + this.$input.find('input:checked').each(function(i, el) { + checked.push($(el).val()); + }); + return checked; + }, + + //collect text of checked boxes + value2htmlFinal: function(value, element) { + var selected = [], html = ''; + if($.isArray(value) && value.length <= this.options.limit) { + for(var i=0; i<value.length; i++){ + item = this.itemByVal(value[i]); + if(item) { + selected.push($('<div>').text(item.text).html()); + } + } + html = selected.join(this.options.viewseparator); + } else { + html = this.options.limitText.replace('{checked}', $.isArray(value) ? value.length : 0).replace('{count}', this.sourceData.length); + } + $(element).html(html); + } + }); + + Checklist.defaults = $.extend({}, $.fn.editableform.types.list.defaults, { + /** + @property tpl + @default <div></div> + **/ + tpl:'<div></div>', + + /** + Separator of values in string when sending to server + + @property separator + @type string + @default ', ' + **/ + separator: ',', + /** + Separator of text when display as element content. + + @property viewseparator + @type string + @default ', ' + **/ + viewseparator: '<br>', + /** + Maximum number of items shown as element content. + If checked more items - <code>limitText</code> will be shown. + + @property limit + @type integer + @default 4 + **/ + limit: 4, + /** + Text shown when count of checked items is greater than <code>limit</code> parameter. + You can use <code>{checked}</code> and <code>count</code> placeholders. + + @property limitText + @type string + @default 'Checked {checked} options of {count}' + **/ + limitText: 'Checked {checked} options of {count}' + }); + + $.fn.editableform.types.checklist = Checklist; + +}(window.jQuery)); \ No newline at end of file diff --git a/src/inputs/date/date.js b/src/inputs/date/date.js index 38680e8..5081db2 100644 --- a/src/inputs/date/date.js +++ b/src/inputs/date/date.js @@ -5,6 +5,7 @@ For localization you can include js file from here: https://github.com/eternicod @class date @extends abstract +@final @example <a href="#" id="dob" data-type="date" data-pk="1" data-url="/post" data-original-title="Select date">15/05/1984</a> <script> diff --git a/src/inputs/dateui/dateui.js b/src/inputs/dateui/dateui.js index 53ee858..38cc2d3 100644 --- a/src/inputs/dateui/dateui.js +++ b/src/inputs/dateui/dateui.js @@ -5,6 +5,7 @@ Do not use it together with bootstrap-datepicker. @class dateui @extends abstract +@final @example <a href="#" id="dob" data-type="date" data-pk="1" data-url="/post" data-original-title="Select date">15/05/1984</a> <script> diff --git a/src/inputs/list.js b/src/inputs/list.js new file mode 100644 index 0000000..8dba070 --- /dev/null +++ b/src/inputs/list.js @@ -0,0 +1,255 @@ +/** +List - abstract class for inputs that have source option loaded from js array or via ajax + +@class list +@extends abstract +**/ +(function ($) { + + var List = function (options) { + + }; + + $.fn.editableform.utils.inherit(List, $.fn.editableform.types.abstract); + + $.extend(List.prototype, { + render: function () { + List.superclass.render.call(this); + var deferred = $.Deferred(); + this.error = null; + this.sourceData = null; + this.prependData = 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) { + var deferred = $.Deferred(); + this.onSourceReady(function () { + this.value2htmlFinal(value, element); + deferred.resolve(); + }, function () { + List.superclass.value2html(this.options.sourceError, element); + deferred.resolve(); + }); + + return deferred.promise(); + }, + + // ------------- additional functions ------------ + + onSourceReady: function (success, error) { + //if allready loaded just call success + if($.isArray(this.sourceData)) { + success.call(this); + return; + } + + // try parse json in single quotes (for double quotes jquery does automatically) + try { + this.options.source = $.fn.editableform.utils.tryParseJson(this.options.source, false); + } catch (e) { + error.call(this); + return; + } + + //loading from url + if (typeof this.options.source === 'string') { + var cacheID = this.options.source + (this.options.name ? '-' + this.options.name : ''), + 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; + 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; + 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 = []; + } + + //loading sourceData from server + $.ajax({ + url: this.options.source, + type: 'get', + cache: false, + data: {name: this.options.name}, + dataType: 'json', + success: $.proxy(function (data) { + cache.loading = false; + // this.options.source = data; + this.sourceData = this.makeArray(data); + if($.isArray(this.sourceData)) { + this.doPrepend(); + //store result in cache + cache.sourceData = this.sourceData; + success.call(this); + $.each(cache.callbacks, function () { this.call(); }); //run success callbacks for other fields + } else { + error.call(this); + $.each(cache.err_callbacks, function () { this.call(); }); //run error callbacks for other fields + } + }, this), + error: $.proxy(function () { + cache.loading = false; + error.call(this); + $.each(cache.err_callbacks, function () { this.call(); }); //run error callbacks for other fields + }, this) + }); + } else { //options as json/array + this.sourceData = this.makeArray(this.options.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)) { + //try parse json in single quotes + this.options.prepend = $.fn.editableform.utils.tryParseJson(this.options.prepend, true); + 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 = [], iterateEl; + if(!data || typeof data === 'string') { + return null; + } + + if($.isArray(data)) { //array + iterateEl = function (k, v) { + obj = {value: k, text: v}; + if(count++ >= 2) { + return false;// exit each if object has more than one value + } + }; + + for(var i = 0; i < data.length; i++) { + if(typeof data[i] === 'object') { + count = 0; + $.each(data[i], iterateEl); + if(count === 1) { + result.push(obj); + } else if(count > 1 && data[i].hasOwnProperty('value') && data[i].hasOwnProperty('text')) { + result.push(data[i]); + } else { + //data contains incorrect objects + } + } else { + result.push({value: i, text: data[i]}); + } + } + } else { //object + $.each(data, function (k, v) { + result.push({value: k, text: v}); + }); + } + return result; + }, + + //search for item by particular value + itemByVal: function(val) { + if($.isArray(this.sourceData)) { + for(i=0; i<this.sourceData.length; i++){ + /*jshint eqeqeq: false*/ + if(this.sourceData[i].value == val) { + /*jshint eqeqeq: true*/ + return this.sourceData[i]; + } + } + } + } + + }); + + List.defaults = $.extend({}, $.fn.editableform.types.abstract.defaults, { + /** + Source data for dropdown list. If string - considered ajax url to load items. Otherwise should be an array. + Array format is: <code>[{value: 1, text: "text"}, {...}]</code><br> + For compability it also supports format <code>{value1: text1, value2: text2 ...}</code> but it does not guarantee elements order. + + @property source + @type string|array|object + @default null + **/ + source:null, + /** + Data automatically prepended to the begining of dropdown list. + + @property prepend + @type string|array|object + @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' + }); + + $.fn.editableform.types.list = List; + +}(window.jQuery)); \ No newline at end of file diff --git a/src/inputs/select.js b/src/inputs/select.js index 8cda196..89b4265 100644 --- a/src/inputs/select.js +++ b/src/inputs/select.js @@ -2,7 +2,8 @@ Select (dropdown) input @class select -@extends abstract +@extends list +@final @example <a href="#" id="status" data-type="select" data-pk="1" data-url="/post" data-original-title="Select status"></a> <script> @@ -25,160 +26,10 @@ $(function(){ this.init('select', options, Select.defaults); }; - $.fn.editableform.utils.inherit(Select, $.fn.editableform.types.abstract); + $.fn.editableform.utils.inherit(Select, $.fn.editableform.types.list); $.extend(Select.prototype, { - render: function () { - Select.superclass.render.call(this); - var deferred = $.Deferred(); - this.error = null; - this.sourceData = null; - this.prependData = null; - this.onSourceReady(function () { - this.renderOptions(); - deferred.resolve(); - }, function () { - this.error = this.options.sourceError; - deferred.resolve(); - }); - - return deferred.promise(); - }, - - html2value: function (html) { - return null; //it's not good idea to set value by text for SELECT. Better set NULL - }, - - value2html: function (value, element) { - var deferred = $.Deferred(); - this.onSourceReady(function () { - var i, text = ''; - if($.isArray(this.sourceData)) { - for(i=0; i<this.sourceData.length; i++){ - /*jshint eqeqeq: false*/ - if(this.sourceData[i].value == value) { - /*jshint eqeqeq: true*/ - text = this.sourceData[i].text; - break; - } - } - } - Select.superclass.value2html(text, element); - deferred.resolve(); - }, function () { - Select.superclass.value2html(this.options.sourceError, element); - deferred.resolve(); - }); - - return deferred.promise(); - }, - - // ------------- additional functions ------------ - - onSourceReady: function (success, error) { - //if allready loaded just call success - if($.isArray(this.sourceData)) { - success.call(this); - return; - } - - // try parse json in single quotes (for double quotes jquery does automatically) - try { - this.options.source = $.fn.editableform.utils.tryParseJson(this.options.source, false); - } catch (e) { - error.call(this); - return; - } - - //loading from url - if (typeof this.options.source === 'string') { - var cacheID = this.options.source + (this.options.name ? '-' + this.options.name : ''), - 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; - 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; - 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 = []; - } - - //loading sourceData from server - $.ajax({ - url: this.options.source, - type: 'get', - cache: false, - data: {name: this.options.name}, - dataType: 'json', - success: $.proxy(function (data) { - cache.loading = false; - // this.options.source = data; - this.sourceData = this.makeArray(data); - if($.isArray(this.sourceData)) { - this.doPrepend(); - //store result in cache - cache.sourceData = this.sourceData; - success.call(this); - $.each(cache.callbacks, function () { this.call(); }); //run success callbacks for other fields - } else { - error.call(this); - $.each(cache.err_callbacks, function () { this.call(); }); //run error callbacks for other fields - } - }, this), - error: $.proxy(function () { - cache.loading = false; - error.call(this); - $.each(cache.err_callbacks, function () { this.call(); }); //run error callbacks for other fields - }, this) - }); - } else { //options as json/array - this.sourceData = this.makeArray(this.options.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)) { - //try parse json in single quotes - this.options.prepend = $.fn.editableform.utils.tryParseJson(this.options.prepend, true); - 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); - } - }, - - renderOptions: function() { + renderList: function() { if(!$.isArray(this.sourceData)) { return; } @@ -187,83 +38,24 @@ $(function(){ this.$input.append($('<option>', {value: this.sourceData[i].value}).text(this.sourceData[i].text)); } }, - - /** - * convert data to array suitable for sourceData, e.g. [{value: 1, text: 'abc'}, {...}] - */ - makeArray: function(data) { - var count, obj, result = [], iterateEl; - if(!data || typeof data === 'string') { - return null; + + value2htmlFinal: function(value, element) { + var text = '', item = this.itemByVal(value); + if(item) { + text = item.text; } - - if($.isArray(data)) { //array - iterateEl = function (k, v) { - obj = {value: k, text: v}; - if(count++ >= 2) { - return false;// exit each if object has more than one value - } - }; - - for(var i = 0; i < data.length; i++) { - if(typeof data[i] === 'object') { - count = 0; - $.each(data[i], iterateEl); - if(count === 1) { - result.push(obj); - } else if(count > 1 && data[i].hasOwnProperty('value') && data[i].hasOwnProperty('text')) { - result.push(data[i]); - } else { - //data contains incorrect objects - } - } else { - result.push({value: i, text: data[i]}); - } - } - } else { //object - $.each(data, function (k, v) { - result.push({value: k, text: v}); - }); - } - return result; - } - + Select.superclass.constructor.superclass.value2html(text, element); + } }); - Select.defaults = $.extend({}, $.fn.editableform.types.abstract.defaults, { + Select.defaults = $.extend({}, $.fn.editableform.types.list.defaults, { /** @property tpl @default <select></select> **/ - tpl:'<select></select>', - /** - Source data for dropdown list. If string - considered ajax url to load items. Otherwise should be an array. - Array format is: <code>[{value: 1, text: "text"}, {...}]</code><br> - For compability it also supports format <code>{value1: text1, value2: text2 ...}</code> but it does not guarantee elements order. - - @property source - @type string|array|object - @default null - **/ - source:null, - /** - Data automatically prepended to the begining of dropdown list. - - @property prepend - @type string|array|object - @default false - **/ - prepend:false, - /** - Error message shown when list cannot be loaded (e.g. ajax error) - - @property sourceError - @type string - @default Error when loading options - **/ - sourceError: 'Error when loading options' + tpl:'<select></select>' }); $.fn.editableform.types.select = Select; -}(window.jQuery)); +}(window.jQuery)); \ No newline at end of file diff --git a/src/inputs/text.js b/src/inputs/text.js index 702c3f6..8d2f259 100644 --- a/src/inputs/text.js +++ b/src/inputs/text.js @@ -3,6 +3,7 @@ Text input @class text @extends abstract +@final @example <a href="#" id="username" data-type="text" data-pk="1">awesome</a> <script> diff --git a/src/inputs/textarea.js b/src/inputs/textarea.js index c4d867e..eb4cd3c 100644 --- a/src/inputs/textarea.js +++ b/src/inputs/textarea.js @@ -3,6 +3,7 @@ Textarea input @class textarea @extends abstract +@final @example <a href="#" id="comments" data-type="textarea" data-pk="1">awesome comment!</a> <script> diff --git a/test/loader.js b/test/loader.js index 136ba5b..ba91063 100644 --- a/test/loader.js +++ b/test/loader.js @@ -31,9 +31,11 @@ function getAssets(f, c, src, libs) { containers+'editable-container.js', element+'editable-element.js', inputs+'abstract.js', + inputs+'list.js', inputs+'text.js', inputs+'textarea.js', - inputs+'select.js' + inputs+'select.js', + inputs+'checklist.js' ], css = [ From 0f795a3882de8a3c3823b75ed6acba6d57b9dfad Mon Sep 17 00:00:00 2001 From: vitalets <noginsk@rambler.ru> Date: Sun, 25 Nov 2012 12:13:49 +0400 Subject: [PATCH 02/24] add checklist to build --- grunt.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/grunt.js b/grunt.js index 84c7cb7..3b66d5b 100644 --- a/grunt.js +++ b/grunt.js @@ -38,9 +38,11 @@ function getFiles() { containers+'editable-container.js', lib+'element/editable-element.js', inputs+'abstract.js', + inputs+'list.js', inputs+'text.js', inputs+'textarea.js', - inputs+'select.js' + inputs+'select.js', + inputs+'checklist.js' ]; //common css files From 0487380535ab9d7db875253638946875e56dbb5c Mon Sep 17 00:00:00 2001 From: vitalets <noginsk@rambler.ru> Date: Sun, 25 Nov 2012 13:22:27 +0400 Subject: [PATCH 03/24] checklist test --- src/editable-form/editable-form-utils.js | 2 +- src/inputs/checklist.js | 12 +-- src/inputs/list.js | 2 +- test/index.html | 1 + test/mocks.js | 34 ++++++++- test/unit/checklist.js | 96 ++++++++++++++++++++++++ test/unit/select.js | 30 -------- 7 files changed, 138 insertions(+), 39 deletions(-) create mode 100644 test/unit/checklist.js diff --git a/src/editable-form/editable-form-utils.js b/src/editable-form/editable-form-utils.js index 8b09f46..3e2eaa6 100644 --- a/src/editable-form/editable-form-utils.js +++ b/src/editable-form/editable-form-utils.js @@ -38,7 +38,7 @@ * 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 (typeof s === 'string' && s.length && s.match(/^[\{\[].*[\}\]]$/)) { if (safe) { try { /*jslint evil: true*/ diff --git a/src/inputs/checklist.js b/src/inputs/checklist.js index 158d509..33fa2b7 100644 --- a/src/inputs/checklist.js +++ b/src/inputs/checklist.js @@ -55,7 +55,7 @@ $(function(){ str2value: function(str) { var reg, value = null; if(typeof str === 'string' && str.length) { - reg = new RegExp('\s*'+$.trim(this.options.separator)+'\s*'); + reg = new RegExp('\\s*'+$.trim(this.options.separator)+'\\s*'); value = str.split(reg); } else if($.isArray(str)) { value = str; @@ -86,9 +86,9 @@ $(function(){ //collect text of checked boxes value2htmlFinal: function(value, element) { - var selected = [], html = ''; - if($.isArray(value) && value.length <= this.options.limit) { - for(var i=0; i<value.length; i++){ + var selected = [], item, i, html = ''; + if($.isArray(value) && value.length <= this.options.limit) { + for(i=0; i<value.length; i++){ item = this.itemByVal(value[i]); if(item) { selected.push($('<div>').text(item.text).html()); @@ -140,9 +140,9 @@ $(function(){ @property limitText @type string - @default 'Checked {checked} options of {count}' + @default 'Selected {checked} of {count}' **/ - limitText: 'Checked {checked} options of {count}' + limitText: 'Selected {checked} of {count}' }); $.fn.editableform.types.checklist = Checklist; diff --git a/src/inputs/list.js b/src/inputs/list.js index 8dba070..43cc74e 100644 --- a/src/inputs/list.js +++ b/src/inputs/list.js @@ -209,7 +209,7 @@ List - abstract class for inputs that have source option loaded from js array or //search for item by particular value itemByVal: function(val) { if($.isArray(this.sourceData)) { - for(i=0; i<this.sourceData.length; i++){ + for(var i=0; i<this.sourceData.length; i++){ /*jshint eqeqeq: false*/ if(this.sourceData[i].value == val) { /*jshint eqeqeq: true*/ diff --git a/test/index.html b/test/index.html index 7181e62..fe1e4df 100644 --- a/test/index.html +++ b/test/index.html @@ -43,6 +43,7 @@ <script src="unit/select.js"></script> <script src="unit/textarea.js"></script> <script src="unit/api.js"></script> + <script src="unit/checklist.js"></script> <script> if(fc.f === 'bootstrap') { loadJs('unit/date.js'); diff --git a/test/mocks.js b/test/mocks.js index c004cfc..46d7a4f 100644 --- a/test/mocks.js +++ b/test/mocks.js @@ -1,7 +1,7 @@ var timeout = 200; $(function () { - $.mockjaxSettings.responseTime = 50; + $.mockjaxSettings.responseTime = 50; $.mockjax({ url: 'post.php', @@ -23,6 +23,38 @@ $(function () { this.responseText = settings; } }); + + + window.groups = { + 0: 'Guest', + 1: 'Service', + 2: 'Customer', + 3: 'Operator', + 4: 'Support', + 5: 'Admin', + 6: '', + '': 'Nothing' + }; + + //groups as array + window.groupsArr = []; + for(var i in groups) { + groupsArr.push({value: i, text: groups[i]}); + } + + window.size = groupsArr.length; + + $.mockjax({ + url: 'groups.php', + responseText: groups + }); + + $.mockjax({ + url: 'groups-error.php', + status: 500, + responseText: 'Internal Server Error' + }); + }); // usefull functions diff --git a/test/unit/checklist.js b/test/unit/checklist.js new file mode 100644 index 0000000..1f4258f --- /dev/null +++ b/test/unit/checklist.js @@ -0,0 +1,96 @@ +$(function () { + + module("checklist", { + setup: function(){ + sfx = $('#qunit-fixture'), + fx = $('#async-fixture'); + $.fn.editable.defaults.name = 'name2'; + //clear cache + $(document).removeData('groups.php-'+$.fn.editable.defaults.name); + $.support.transition = false; + } + }); + + asyncTest("should load options, set correct value and save new value", function () { + var sep = '-', + newValue, + e = $('<a href="#" data-type="checklist" data-url="post.php"></a>').appendTo(fx).editable({ + pk: 1, + source: groupsArr, + value: [2, 3], + viewseparator: sep + }); + + equal(e.text(), groups[2]+sep+groups[3], 'autotext ok'); + + e.click(); + var p = tip(e); + equal(p.find('input[type="checkbox"]').length, groupsArr.length, 'checkboxes rendered'); + equal(p.find('input[type="checkbox"]:checked').length, 2, 'checked count ok'); + equal(p.find('input[type="checkbox"]:checked').eq(0).val(), 2, '1st checked'); + equal(p.find('input[type="checkbox"]:checked').eq(1).val(), 3, '2nd checked'); + + //set new value + p.find('input[type="checkbox"]:checked').eq(0).click(); + p.find('input[type="checkbox"]').first().click(); + newValue = p.find('input[type="checkbox"]').first().val(); + + //submit + p.find('form').submit(); + + setTimeout(function() { + ok(!p.is(':visible'), 'popup closed'); + + equal(e.data('editable').value.join(''), [newValue, 3].join(''), 'new value ok') + equal(e.text(), groups[newValue]+sep+groups[3], 'new text ok'); + + // open container again to see what checked + e.click() + p = tip(e); + + equal(p.find('input[type="checkbox"]').length, groupsArr.length, 'checkboxes rendered'); + equal(p.find('input[type="checkbox"]:checked').length, 2, 'checked count ok'); + equal(p.find('input[type="checkbox"]:checked').eq(0).val(), newValue, '1st checked'); + equal(p.find('input[type="checkbox"]:checked').eq(1).val(), 3, '2nd checked'); + + e.remove(); + start(); + }, timeout); + }); + + asyncTest("test limit", function () { + var e = $('<a href="#" data-type="checklist" data-value="2,3" data-url="post.php"></a>').appendTo(fx).editable({ + pk: 1, + source: groupsArr, + limit: 1, + limitText: '{checked} of {count}' + }); + + equal(e.text(), '2 of '+groupsArr.length, 'autotext ok'); + + e.click(); + var p = tip(e); + + equal(p.find('input[type="checkbox"]:checked').length, 2, 'checked count ok'); + equal(p.find('input[type="checkbox"]:checked').eq(0).val(), 2, '1st checked'); + equal(p.find('input[type="checkbox"]:checked').eq(1).val(), 3, '2nd checked'); + + //set new value + p.find('input[type="checkbox"]').first().click(); + newValue = p.find('input[type="checkbox"]').first().val(); + + //submit + p.find('form').submit(); + + setTimeout(function() { + ok(!p.is(':visible'), 'popup closed'); + + equal(e.text(), '3 of '+groupsArr.length, 'autotext ok'); + + e.remove(); + start(); + }, timeout); + }); + + +}); \ No newline at end of file diff --git a/test/unit/select.js b/test/unit/select.js index a4a1256..dc67931 100644 --- a/test/unit/select.js +++ b/test/unit/select.js @@ -1,34 +1,4 @@ $(function () { - - window.groups = { - 0: 'Guest', - 1: 'Service', - 2: 'Customer', - 3: 'Operator', - 4: 'Support', - 5: 'Admin', - 6: '', - '': 'Nothing' - }; - - //groups as array - window.groupsArr = []; - for(var i in groups) { - groupsArr.push({value: i, text: groups[i]}); - } - - window.size = groupsArr.length; - - $.mockjax({ - url: 'groups.php', - responseText: groups - }); - - $.mockjax({ - url: 'groups-error.php', - status: 500, - responseText: 'Internal Server Error' - }); module("select", { setup: function(){ From 69628f131db0e32ce27dc3baa2766600bbf6aa55 Mon Sep 17 00:00:00 2001 From: vitalets <noginsk@rambler.ru> Date: Sun, 25 Nov 2012 13:32:39 +0400 Subject: [PATCH 04/24] comments fixes for docs --- src/editable-form/editable-form.js | 2 +- src/inputs/checklist.js | 6 +++--- src/inputs/dateui/dateui.js | 4 ++-- src/inputs/list.js | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/editable-form/editable-form.js b/src/editable-form/editable-form.js index d93042a..57cc38e 100644 --- a/src/editable-form/editable-form.js +++ b/src/editable-form/editable-form.js @@ -301,7 +301,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. /* see also defaults for input */ /** - Type of input. Can be <code>text|textarea|select|date</code> + Type of input. Can be <code>text|textarea|select|date|checklist</code> @property type @type string diff --git a/src/inputs/checklist.js b/src/inputs/checklist.js index 33fa2b7..2dd4f75 100644 --- a/src/inputs/checklist.js +++ b/src/inputs/checklist.js @@ -1,11 +1,11 @@ /** -Checklist input. Internally value stored as array. +List of checkboxes. Internally value stored as javascript array. @class checklist @extends list @final @example -<a href="#" id="status" data-type="checklist" data-pk="1" data-url="/post" data-original-title="Select options"></a> +<a href="#" id="options" data-type="checklist" data-pk="1" data-url="/post" data-original-title="Select options"></a> <script> $(function(){ $('#options').editable({ @@ -122,7 +122,7 @@ $(function(){ @property viewseparator @type string - @default ', ' + @default '<br>' **/ viewseparator: '<br>', /** diff --git a/src/inputs/dateui/dateui.js b/src/inputs/dateui/dateui.js index 38cc2d3..266d15c 100644 --- a/src/inputs/dateui/dateui.js +++ b/src/inputs/dateui/dateui.js @@ -1,7 +1,7 @@ /** jQuery UI Datepicker. -Description and examples: http://jqueryui.com/datepicker. -Do not use it together with bootstrap-datepicker. +Description and examples: http://jqueryui.com/datepicker. +This input is also accessible as **date** type. Do not use it together with __bootstrap-datepicker__ as both apply <code>$().datepicker()</code> method. @class dateui @extends abstract diff --git a/src/inputs/list.js b/src/inputs/list.js index 43cc74e..1eeeeb2 100644 --- a/src/inputs/list.js +++ b/src/inputs/list.js @@ -225,7 +225,7 @@ List - abstract class for inputs that have source option loaded from js array or /** Source data for dropdown list. If string - considered ajax url to load items. Otherwise should be an array. Array format is: <code>[{value: 1, text: "text"}, {...}]</code><br> - For compability it also supports format <code>{value1: text1, value2: text2 ...}</code> but it does not guarantee elements order. + For compability it also supports format <code>{value1: "text1", value2: "text2" ...}</code> but it does not guarantee elements order. @property source @type string|array|object From 40eea34004c1593e1e5cc8c967e74bcb1079f4fd Mon Sep 17 00:00:00 2001 From: vitalets <noginsk@rambler.ru> Date: Sun, 25 Nov 2012 15:11:18 +0400 Subject: [PATCH 05/24] add class editable-input --- src/editable-form/editable-form.css | 6 ++---- src/inputs/abstract.js | 1 + src/inputs/checklist.js | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/editable-form/editable-form.css b/src/editable-form/editable-form.css index 0b2b07e..0ef39e5 100644 --- a/src/editable-form/editable-form.css +++ b/src/editable-form/editable-form.css @@ -23,12 +23,11 @@ color: red; } -.editableform input, -.editableform select, -.editableform textarea { +.editableform .editable-input { vertical-align: top; display: inline-block; width: auto; /* bootstrap-responsive has width: 100% that breakes layout */ + float: left; } .editableform textarea { @@ -36,7 +35,6 @@ } .editableform .editable-date { - float: left; padding: 0; margin: 0 0 9px 0; } \ No newline at end of file diff --git a/src/inputs/abstract.js b/src/inputs/abstract.js index b0320cd..bb2b243 100644 --- a/src/inputs/abstract.js +++ b/src/inputs/abstract.js @@ -28,6 +28,7 @@ To create your own input you should inherit from this class. **/ render: function() { this.$input = $(this.options.tpl); + this.$input.addClass('editable-input'); if(this.options.inputclass) { this.$input.addClass(this.options.inputclass); } diff --git a/src/inputs/checklist.js b/src/inputs/checklist.js index 2dd4f75..491b095 100644 --- a/src/inputs/checklist.js +++ b/src/inputs/checklist.js @@ -1,5 +1,5 @@ /** -List of checkboxes. Internally value stored as javascript array. +List of checkboxes. Internally value stored as javascript array of values. @class checklist @extends list @@ -147,4 +147,4 @@ $(function(){ $.fn.editableform.types.checklist = Checklist; -}(window.jQuery)); \ No newline at end of file +}(window.jQuery)); From 18cbb07090f06eae90dc530d8ff0b09d8a558606 Mon Sep 17 00:00:00 2001 From: vitalets <noginsk@rambler.ru> Date: Sun, 25 Nov 2012 15:14:40 +0400 Subject: [PATCH 06/24] changelog checklist --- CHANGELOG.txt | 1 + src/inputs/checklist.js | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index da494b4..d409431 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -3,6 +3,7 @@ X-editable changelog Version 1.0.2 wip ---------------------------- +[enh] new input type: checklist (vitalets) [enh] updated docs: inputs dropdown menu, global templates section (vitalets) diff --git a/src/inputs/checklist.js b/src/inputs/checklist.js index 491b095..4bd634a 100644 --- a/src/inputs/checklist.js +++ b/src/inputs/checklist.js @@ -11,9 +11,9 @@ $(function(){ $('#options').editable({ value: [2, 3], source: [ - {value: 1, text: 'Active'}, - {value: 2, text: 'Blocked'}, - {value: 3, text: 'Deleted'} + {value: 1, text: 'option1'}, + {value: 2, text: 'option2'}, + {value: 3, text: 'option3'} ] } }); From 2842c6dce355d3c4821258623d66b4401233ed0c Mon Sep 17 00:00:00 2001 From: vitalets <noginsk@rambler.ru> Date: Sun, 25 Nov 2012 16:02:57 +0400 Subject: [PATCH 07/24] when source is simple array - key = text --- src/inputs/list.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inputs/list.js b/src/inputs/list.js index 1eeeeb2..33be516 100644 --- a/src/inputs/list.js +++ b/src/inputs/list.js @@ -195,7 +195,7 @@ List - abstract class for inputs that have source option loaded from js array or //data contains incorrect objects } } else { - result.push({value: i, text: data[i]}); + result.push({value: data[i], text: data[i]}); } } } else { //object From c6da2ed11b8eb5a905703a705f2e2a6fbd863c7f Mon Sep 17 00:00:00 2001 From: vitalets <noginsk@rambler.ru> Date: Sun, 25 Nov 2012 16:05:43 +0400 Subject: [PATCH 08/24] fix test for simple array --- test/unit/select.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/select.js b/test/unit/select.js index dc67931..fec4a8e 100644 --- a/test/unit/select.js +++ b/test/unit/select.js @@ -81,7 +81,7 @@ $(function () { test("load options from simple array", function () { var arr = ['q', 'w', 'x'], - e = $('<a href="#" data-type="select" data-value="2" data-url="post.php">customer</a>').appendTo('#qunit-fixture').editable({ + e = $('<a href="#" data-type="select" data-value="x" data-url="post.php">customer</a>').appendTo('#qunit-fixture').editable({ pk: 1, autotext: true, source: arr @@ -92,7 +92,7 @@ $(function () { ok(p.is(':visible'), 'popover visible') ok(p.find('select').length, 'select exists') equal(p.find('select').find('option').length, arr.length, 'options loaded') - equal(p.find('select').val(), 2, 'selected value correct') + equal(p.find('select').val(), 'x', 'selected value correct') p.find('button[type=button]').click(); ok(!p.is(':visible'), 'popover was removed'); }) From c9f2f31adddc02120b751c97479a516682830b56 Mon Sep 17 00:00:00 2001 From: vitalets <noginsk@rambler.ru> Date: Mon, 26 Nov 2012 12:43:17 +0400 Subject: [PATCH 09/24] new form template --- src/editable-form/editable-form-bootstrap.js | 7 +++- src/editable-form/editable-form-jqueryui.js | 13 +++---- src/editable-form/editable-form.css | 19 +++++++--- src/editable-form/editable-form.js | 40 +++++++++++--------- 4 files changed, 48 insertions(+), 31 deletions(-) diff --git a/src/editable-form/editable-form-bootstrap.js b/src/editable-form/editable-form-bootstrap.js index ff07282..350a0ac 100644 --- a/src/editable-form/editable-form-bootstrap.js +++ b/src/editable-form/editable-form-bootstrap.js @@ -5,10 +5,13 @@ Editableform based on Twitter Bootstrap //form template $.fn.editableform.template = '<form class="form-inline editableform"><div class="control-group">' + - ' <button type="submit" class="btn btn-primary"><i class="icon-ok icon-white"></i></button> <button type="button" class="btn clearfix"><i class="icon-ban-circle"></i></button>' + - '<div style="clear:both"><span class="help-block editable-error-block"></span></div>' + + '<div class="editable-input"></div><div class="editable-buttons"></div>' + + '<div class="help-block editable-error-block"></div>' + '</div></form>'; + //buttons + $.fn.editableform.buttons = '<button type="submit" class="btn btn-primary"><i class="icon-ok icon-white"></i></button><button type="button" class="btn clearfix"><i class="icon-ban-circle"></i></button>'; + //error classes $.fn.editableform.errorGroupClass = 'error'; $.fn.editableform.errorBlockClass = null; diff --git a/src/editable-form/editable-form-jqueryui.js b/src/editable-form/editable-form-jqueryui.js index e11e331..7ae3670 100644 --- a/src/editable-form/editable-form-jqueryui.js +++ b/src/editable-form/editable-form-jqueryui.js @@ -7,8 +7,9 @@ Editableform based on jQuery UI initTemplate: function() { this.$form = $($.fn.editableform.template); - //init buttons - this.$form.find('button[type=submit]').button({ + //buttons + this.$form.find('div.editable-buttons').append($.fn.editableform.buttons); + this.$form.find('button[type=submit]').button({ icons: { primary: "ui-icon-check" }, text: false }); @@ -16,14 +17,12 @@ Editableform based on jQuery UI icons: { primary: "ui-icon-cancel" }, text: false }); + } }); - //form template - $.fn.editableform.template = '<form class="editableform"><div class="control-group">' + - ' <button type="submit" style="height: 24px">submit</button> <button type="button" style="height: 24px">cancel</button></div>' + - '<div class="editable-error-block"></div>' + - '</form>'; + //buttons + $.fn.editableform.buttons = '<button type="submit" style="height: 24px">submit</button><button type="button" style="height: 24px">cancel</button>'; //error classes $.fn.editableform.errorGroupClass = null; diff --git a/src/editable-form/editable-form.css b/src/editable-form/editable-form.css index 0ef39e5..008cf05 100644 --- a/src/editable-form/editable-form.css +++ b/src/editable-form/editable-form.css @@ -1,6 +1,15 @@ .editableform, .editableform div.control-group { - margin-bottom: 0; + margin-bottom: 0; + overflow: auto; +} + +.editable-buttons { + float: left; +} + +.editable-buttons button { + margin-left: 7px; } .editableform-loading { @@ -14,8 +23,7 @@ .editable-error-block { max-width: 300px; - margin-top: 3px; - margin-bottom: 0; + margin: 0; clear: both; } @@ -23,11 +31,12 @@ color: red; } -.editableform .editable-input { +.editable-input { vertical-align: top; display: inline-block; width: auto; /* bootstrap-responsive has width: 100% that breakes layout */ float: left; + padding-bottom: 9px; } .editableform textarea { @@ -36,5 +45,5 @@ .editableform .editable-date { padding: 0; - margin: 0 0 9px 0; + margin: 0; } \ No newline at end of file diff --git a/src/editable-form/editable-form.js b/src/editable-form/editable-form.js index 57cc38e..738d995 100644 --- a/src/editable-form/editable-form.js +++ b/src/editable-form/editable-form.js @@ -34,6 +34,9 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. }, initTemplate: function() { this.$form = $($.fn.editableform.template); + + //buttons + this.$form.find('div.editable-buttons').append($.fn.editableform.buttons); }, /** Renders editableform @@ -57,8 +60,14 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. //render input $.when(this.input.render()) .then($.proxy(function () { - //place input - this.$form.find('div.control-group').prepend(this.input.$input); + //input + this.$form.find('div.editable-input').append(this.input.$input); + + //clear link + if(this.input.$clear) { + this.$form.find('div.editable-input').append(this.input.$clear); + } + //attach 'cancel' handler this.$form.find('button[type=button]').click($.proxy(this.cancel, this)); //append form to container @@ -94,19 +103,11 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. this.$element.triggerHandler('cancel'); }, showLoading: function() { - var fw, fh, iw, ih; + var fw, fh, iw, bh; //set loading size equal to form if(this.$form) { - fh = this.$form.outerHeight() || 0; - fw = this.$form.outerWidth() || 0; - ih = (this.input && this.input.$input.outerHeight()) || 0; - iw = (this.input && this.input.$input.outerWidth()) || 0; - if(fh || ih) { - this.$loading.height(fh > ih ? fh : ih); - } - if(fw || iw) { - this.$loading.width(fw > iw ? fw : iw); - } + this.$loading.width(this.$form.outerWidth()); + this.$loading.height(this.$form.outerHeight()); this.$form.hide(); } this.$loading.show(); @@ -406,14 +407,19 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. 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">' + - ' <button type="submit">Ok</button> <button type="button">Cancel</button></div>' + - '<div class="editable-error-block"></div>' + - '</form>'; + $.fn.editableform.template = '<form class="form-inline editableform">'+ + '<div class="control-group">' + + '<div class="editable-input"></div><div class="editable-buttons"></div>'+ + '</div>' + + '<div class="editable-error-block"></div>' + + '</form>'; //loading div $.fn.editableform.loading = '<div class="editableform-loading"></div>'; + //buttons + $.fn.editableform.buttons = '<button type="submit">Ok</button><button type="button">Cancel</button>'; + //error class attahced to control-group $.fn.editableform.errorGroupClass = null; From 999bbe25df67eae35d4a28b51c2346ec5e2a518b Mon Sep 17 00:00:00 2001 From: vitalets <noginsk@rambler.ru> Date: Mon, 26 Nov 2012 13:01:04 +0400 Subject: [PATCH 10/24] new form template fixes --- src/editable-form/editable-form-bootstrap.js | 17 ++++++---- src/editable-form/editable-form-jqueryui.js | 33 +++++++++----------- src/editable-form/editable-form.css | 5 +++ src/editable-form/editable-form.js | 5 +-- 4 files changed, 34 insertions(+), 26 deletions(-) diff --git a/src/editable-form/editable-form-bootstrap.js b/src/editable-form/editable-form-bootstrap.js index 350a0ac..f6a5273 100644 --- a/src/editable-form/editable-form-bootstrap.js +++ b/src/editable-form/editable-form-bootstrap.js @@ -3,14 +3,19 @@ Editableform based on Twitter Bootstrap */ (function ($) { - //form template - $.fn.editableform.template = '<form class="form-inline editableform"><div class="control-group">' + - '<div class="editable-input"></div><div class="editable-buttons"></div>' + - '<div class="help-block editable-error-block"></div>' + - '</div></form>'; + $.extend($.fn.editableform.Constructor.prototype, { + initTemplate: function() { + this.$form = $($.fn.editableform.template); + this.$form.find('.editable-error-block').addClass('help-block'); + + //buttons + this.$form.find('div.editable-buttons').append($.fn.editableform.buttons); + } + }); //buttons - $.fn.editableform.buttons = '<button type="submit" class="btn btn-primary"><i class="icon-ok icon-white"></i></button><button type="button" class="btn clearfix"><i class="icon-ban-circle"></i></button>'; + $.fn.editableform.buttons = '<button type="submit" class="btn btn-primary"><i class="icon-ok icon-white"></i></button>'+ + '<button type="button" class="btn clearfix"><i class="icon-ban-circle"></i></button>'; //error classes $.fn.editableform.errorGroupClass = 'error'; diff --git a/src/editable-form/editable-form-jqueryui.js b/src/editable-form/editable-form-jqueryui.js index 7ae3670..c0b6683 100644 --- a/src/editable-form/editable-form-jqueryui.js +++ b/src/editable-form/editable-form-jqueryui.js @@ -4,26 +4,23 @@ Editableform based on jQuery UI (function ($) { $.extend($.fn.editableform.Constructor.prototype, { - initTemplate: function() { - this.$form = $($.fn.editableform.template); - - //buttons - this.$form.find('div.editable-buttons').append($.fn.editableform.buttons); - this.$form.find('button[type=submit]').button({ - icons: { primary: "ui-icon-check" }, - text: false - }); - this.$form.find('button[type=button]').button({ - icons: { primary: "ui-icon-cancel" }, - text: false - }); - - } + initTemplate: function() { + this.$form = $($.fn.editableform.template); + + //buttons + this.$form.find('div.editable-buttons').append($.fn.editableform.buttons); + this.$form.find('button[type=submit]').button({ + icons: { primary: "ui-icon-check" }, + text: false + }).removeAttr('title'); + this.$form.find('button[type=button]').button({ + icons: { primary: "ui-icon-cancel" }, + text: false + }).removeAttr('title'); + + } }); - //buttons - $.fn.editableform.buttons = '<button type="submit" style="height: 24px">submit</button><button type="button" style="height: 24px">cancel</button>'; - //error classes $.fn.editableform.errorGroupClass = null; $.fn.editableform.errorBlockClass = 'ui-state-error'; diff --git a/src/editable-form/editable-form.css b/src/editable-form/editable-form.css index 008cf05..35f0eb0 100644 --- a/src/editable-form/editable-form.css +++ b/src/editable-form/editable-form.css @@ -12,6 +12,11 @@ margin-left: 7px; } +/*for jquery-ui buttons need set height to look more pretty*/ +.editable-buttons button.ui-button { + height: 24px; +} + .editableform-loading { background: url('img/loading.gif') center center no-repeat; height: 25px; diff --git a/src/editable-form/editable-form.js b/src/editable-form/editable-form.js index 738d995..4dd82f2 100644 --- a/src/editable-form/editable-form.js +++ b/src/editable-form/editable-form.js @@ -410,15 +410,16 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. $.fn.editableform.template = '<form class="form-inline editableform">'+ '<div class="control-group">' + '<div class="editable-input"></div><div class="editable-buttons"></div>'+ + '<div class="editable-error-block"></div>' + '</div>' + - '<div class="editable-error-block"></div>' + '</form>'; //loading div $.fn.editableform.loading = '<div class="editableform-loading"></div>'; //buttons - $.fn.editableform.buttons = '<button type="submit">Ok</button><button type="button">Cancel</button>'; + $.fn.editableform.buttons = '<button type="submit">Ok</button>'+ + '<button type="button">Cancel</button>'; //error class attahced to control-group $.fn.editableform.errorGroupClass = null; From 2438ea8da10b6188ec54099359af82b31e84c8ca Mon Sep 17 00:00:00 2001 From: vitalets <noginsk@rambler.ru> Date: Mon, 26 Nov 2012 13:36:29 +0400 Subject: [PATCH 11/24] clear link in date input --- CHANGELOG.txt | 4 ++- src/editable-form/editable-form.css | 5 ++++ src/editable-form/editable-form.js | 4 +-- src/inputs/abstract.js | 13 +++++++-- src/inputs/date/date.js | 26 +++++++++++++++-- src/inputs/text.js | 2 +- test/unit/date.js | 44 ++++++++++++++++++++++++++++- 7 files changed, 89 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index d409431..c15f408 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,8 +1,10 @@ X-editable changelog ============================= -Version 1.0.2 wip +Version 1.1.0 wip ---------------------------- +[enh] 'clear' button added in date (vitalets) +[enh] form template changed: added DIV.editable-input, DIV.editable.buttons and $.fn.editableform buttons (vitalets) [enh] new input type: checklist (vitalets) [enh] updated docs: inputs dropdown menu, global templates section (vitalets) diff --git a/src/editable-form/editable-form.css b/src/editable-form/editable-form.css index 35f0eb0..7a5b7b0 100644 --- a/src/editable-form/editable-form.css +++ b/src/editable-form/editable-form.css @@ -51,4 +51,9 @@ .editableform .editable-date { padding: 0; margin: 0; +} + +.editable-clear { + float: right; + font-size: 0.9em; } \ No newline at end of file diff --git a/src/editable-form/editable-form.js b/src/editable-form/editable-form.js index 4dd82f2..854e40d 100644 --- a/src/editable-form/editable-form.js +++ b/src/editable-form/editable-form.js @@ -418,8 +418,8 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. $.fn.editableform.loading = '<div class="editableform-loading"></div>'; //buttons - $.fn.editableform.buttons = '<button type="submit">Ok</button>'+ - '<button type="button">Cancel</button>'; + $.fn.editableform.buttons = '<button type="submit">ok</button>'+ + '<button type="button">cancel</button>'; //error class attahced to control-group $.fn.editableform.errorGroupClass = null; diff --git a/src/inputs/abstract.js b/src/inputs/abstract.js index bb2b243..a40d742 100644 --- a/src/inputs/abstract.js +++ b/src/inputs/abstract.js @@ -18,6 +18,7 @@ To create your own input you should inherit from this class. this.type = type; this.options = $.extend({}, defaults, options); this.$input = null; + this.$clear = null; this.error = null; }, @@ -28,14 +29,13 @@ To create your own input you should inherit from this class. **/ render: function() { this.$input = $(this.options.tpl); - this.$input.addClass('editable-input'); if(this.options.inputclass) { this.$input.addClass(this.options.inputclass); } if (this.options.placeholder) { this.$input.attr('placeholder', this.options.placeholder); - } + } }, /** @@ -111,6 +111,15 @@ To create your own input you should inherit from this class. if(this.$input.is(':visible')) { this.$input.focus(); } + }, + + /** + Creares input. + + @method clear() + **/ + clear: function() { + this.$input.val(null); } }; diff --git a/src/inputs/date/date.js b/src/inputs/date/date.js index 5081db2..b16f57e 100644 --- a/src/inputs/date/date.js +++ b/src/inputs/date/date.js @@ -54,6 +54,14 @@ $(function(){ render: function () { Date.superclass.render.call(this); this.$input.datepicker(this.options.datepicker); + + if(this.options.clear) { + this.$clear = $('<a href="#">').addClass('editable-clear').html(this.options.clear).click($.proxy(function(e){ + e.preventDefault(); + e.stopPropagation(); + this.clear(); + }, this)); + } }, value2html: function(value, element) { @@ -82,7 +90,12 @@ $(function(){ }, activate: function() { - } + }, + + clear: function() { + this.$input.data('datepicker').date = null; + this.$input.find('.active').removeClass('active'); + } }); @@ -131,7 +144,16 @@ $(function(){ weekStart: 0, startView: 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: '× clear' }); $.fn.editableform.types.date = Date; diff --git a/src/inputs/text.js b/src/inputs/text.js index 8d2f259..8557c2a 100644 --- a/src/inputs/text.js +++ b/src/inputs/text.js @@ -25,8 +25,8 @@ $(function(){ $.extend(Text.prototype, { activate: function() { if(this.$input.is(':visible')) { - $.fn.editableform.utils.setCursorPosition(this.$input.get(0), this.$input.val().length); this.$input.focus(); + $.fn.editableform.utils.setCursorPosition(this.$input.get(0), this.$input.val().length); } } }); diff --git a/test/unit/date.js b/test/unit/date.js index fec2cc7..e2ab4cc 100644 --- a/test/unit/date.js +++ b/test/unit/date.js @@ -14,7 +14,7 @@ $(function () { return dpg.formatDate(date, dpg.parseFormat(format), 'en'); } - asyncTest("popover should contain datepicker with value and save new entered date", function () { + asyncTest("container should contain datepicker with value and save new entered date", function () { expect(9); $.fn.editableform.types.date.defaults.datepicker.weekStart = 1; @@ -130,5 +130,47 @@ $(function () { p.find('button[type=button]').click(); ok(!p.is(':visible'), 'popover closed'); }); + + asyncTest("clear button", function () { + var d = '15.05.1984', + e = $('<a href="#" data-type="date" data-pk="1" data-url="post-date-clear.php">'+d+'</a>').appendTo(fx).editable({ + format: f, + clear: 'abc' + }); + + $.mockjax({ + url: 'post-date-clear.php', + response: function(settings) { + equal(settings.data.value, '', 'submitted value correct'); + } + }); + + equal(frmt(e.data('editable').value, 'dd.mm.yyyy'), d, 'value correct'); + + e.click(); + var p = tip(e); + ok(p.find('.datepicker').is(':visible'), 'datepicker exists'); + + equal(frmt(e.data('editable').value, f), d, 'day set correct'); + equal(p.find('td.day.active').text(), 15, 'day shown correct'); + + var clear = p.find('.editable-clear'); + equal(clear.text(), 'abc', 'clear link shown'); + + //click clear + clear.click(); + ok(!p.find('td.day.active').length, 'no active day'); + + p.find('form').submit(); + + setTimeout(function() { + ok(!p.is(':visible'), 'popover closed'); + equal(e.data('editable').value, null, 'null saved to value'); + equal(e.text(), e.data('editable').options.emptytext, 'empty text shown'); + e.remove(); + start(); + }, timeout); + + }); }); \ No newline at end of file From bd64464a5d46e6c1a3d4d44fd200e1f1c2d4a032 Mon Sep 17 00:00:00 2001 From: vitalets <noginsk@rambler.ru> Date: Mon, 26 Nov 2012 15:32:13 +0400 Subject: [PATCH 12/24] clear button in dateui --- CHANGELOG.txt | 2 +- src/editable-form/editable-form.css | 1 + src/inputs/dateui/dateui.js | 25 ++++++++++++++-- test/unit/dateui.js | 45 +++++++++++++++++++++++++++-- 4 files changed, 68 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index c15f408..d9b3c8b 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -3,7 +3,7 @@ X-editable changelog Version 1.1.0 wip ---------------------------- -[enh] 'clear' button added in date (vitalets) +[enh] 'clear' button added in date and dateui (vitalets) [enh] form template changed: added DIV.editable-input, DIV.editable.buttons and $.fn.editableform buttons (vitalets) [enh] new input type: checklist (vitalets) [enh] updated docs: inputs dropdown menu, global templates section (vitalets) diff --git a/src/editable-form/editable-form.css b/src/editable-form/editable-form.css index 7a5b7b0..6c18918 100644 --- a/src/editable-form/editable-form.css +++ b/src/editable-form/editable-form.css @@ -56,4 +56,5 @@ .editable-clear { float: right; font-size: 0.9em; + text-decoration: none; } \ No newline at end of file diff --git a/src/inputs/dateui/dateui.js b/src/inputs/dateui/dateui.js index 266d15c..986726a 100644 --- a/src/inputs/dateui/dateui.js +++ b/src/inputs/dateui/dateui.js @@ -52,6 +52,14 @@ $(function(){ render: function () { DateUI.superclass.render.call(this); this.$input.datepicker(this.options.datepicker); + + if(this.options.clear) { + this.$clear = $('<a href="#">').addClass('editable-clear').html(this.options.clear).click($.proxy(function(e){ + e.preventDefault(); + e.stopPropagation(); + this.clear(); + }, this)); + } }, value2html: function(value, element) { @@ -100,7 +108,11 @@ $(function(){ }, activate: function() { - } + }, + + clear: function() { + this.$input.datepicker('setDate', null); + } }); @@ -150,7 +162,16 @@ $(function(){ firstDay: 0, changeYear: true, changeMonth: true - } + }, + /** + 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: '× clear' }); $.fn.editableform.types.dateui = DateUI; diff --git a/test/unit/dateui.js b/test/unit/dateui.js index 1fc14fa..91c22fb 100644 --- a/test/unit/dateui.js +++ b/test/unit/dateui.js @@ -60,7 +60,7 @@ $(function () { test("viewformat, init by value", function () { var dview = '15/05/1984', - d = '1984-05-15', + d = '1984-05-15', e = $('<a href="#" data-type="date" data-pk="1" data-weekstart="1" data-value="'+d+'"></a>').appendTo('#qunit-fixture').editable({ format: 'yyyy-mm-dd', viewformat: 'dd/mm/yyyy' @@ -80,6 +80,47 @@ $(function () { p.find('button[type=button]').click(); ok(!p.is(':visible'), 'popover closed'); - }); + }); + + asyncTest("clear button", function () { + var d = '15.05.1984', + f = 'dd.mm.yyyy', + e = $('<a href="#" data-type="date" data-pk="1" data-url="post-date-clear.php">'+d+'</a>').appendTo(fx).editable({ + format: f, + clear: 'abc' + }); + + $.mockjax({ + url: 'post-date-clear.php', + response: function(settings) { + equal(settings.data.value, '', 'submitted value correct'); + } + }); + + equal(frmt(e.data('editable').value, 'dd.mm.yyyy'), d, 'value correct'); + + e.click(); + var p = tip(e); + ok(p.find('.ui-datepicker').is(':visible'), 'datepicker exists'); + + equal(frmt(e.data('editable').value, f), d, 'day set correct'); + equal(p.find('a.ui-state-active').text(), 15, 'day shown correct'); + + var clear = p.find('.editable-clear'); + equal(clear.text(), 'abc', 'clear link shown'); + + //click clear + clear.click(); + p.find('form').submit(); + + setTimeout(function() { + ok(!p.is(':visible'), 'popover closed'); + equal(e.data('editable').value, null, 'null saved to value'); + equal(e.text(), e.data('editable').options.emptytext, 'empty text shown'); + e.remove(); + start(); + }, timeout); + + }); }); \ No newline at end of file From 612ecc5e55a762ecf739a08b642bb1ac4d4baa08 Mon Sep 17 00:00:00 2001 From: vitalets <noginsk@rambler.ru> Date: Mon, 26 Nov 2012 16:21:55 +0400 Subject: [PATCH 13/24] name is not requred anymore fixes #9 --- CHANGELOG.txt | 1 + src/element/editable-element.js | 6 +----- src/inputs/list.js | 5 ++--- test/unit/api.js | 1 - test/unit/checklist.js | 5 +---- test/unit/date.js | 1 - test/unit/dateui.js | 1 - test/unit/select.js | 36 ++++++++++++++++++++------------- test/unit/text.js | 1 - test/unit/textarea.js | 4 ++-- 10 files changed, 29 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index d9b3c8b..07824fc 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -3,6 +3,7 @@ X-editable changelog Version 1.1.0 wip ---------------------------- +[enh #9] 'name' or 'id' is not required anymore (vitalets) [enh] 'clear' button added in date and dateui (vitalets) [enh] form template changed: added DIV.editable-input, DIV.editable.buttons and $.fn.editableform buttons (vitalets) [enh] new input type: checklist (vitalets) diff --git a/src/element/editable-element.js b/src/element/editable-element.js index ed282fd..fe581cd 100644 --- a/src/element/editable-element.js +++ b/src/element/editable-element.js @@ -29,12 +29,8 @@ Makes editable any HTML element on the page. Applied as jQuery method. return; } - //name must be defined + //name this.options.name = this.options.name || this.$element.attr('id'); - if (!this.options.name) { - $.error('You must define name (or id) for Editable element'); - return; - } //create input of specified type. Input will be used for converting value, not in form if(typeof $.fn.editableform.types[this.options.type] === 'function') { diff --git a/src/inputs/list.js b/src/inputs/list.js index 33be516..fc8412c 100644 --- a/src/inputs/list.js +++ b/src/inputs/list.js @@ -93,17 +93,16 @@ List - abstract class for inputs that have source option loaded from js array or cache.callbacks = []; cache.err_callbacks = []; } - + //loading sourceData from server $.ajax({ url: this.options.source, type: 'get', cache: false, - data: {name: this.options.name}, + data: this.options.name ? {name: this.options.name} : {}, dataType: 'json', success: $.proxy(function (data) { cache.loading = false; - // this.options.source = data; this.sourceData = this.makeArray(data); if($.isArray(this.sourceData)) { this.doPrepend(); diff --git a/test/unit/api.js b/test/unit/api.js index dfb24fb..313e682 100644 --- a/test/unit/api.js +++ b/test/unit/api.js @@ -3,7 +3,6 @@ $(function () { module("api", { setup: function(){ fx = $('#async-fixture'); - delete $.fn.editable.defaults.name; $.support.transition = false; } }); diff --git a/test/unit/checklist.js b/test/unit/checklist.js index 1f4258f..294d2a5 100644 --- a/test/unit/checklist.js +++ b/test/unit/checklist.js @@ -4,9 +4,6 @@ $(function () { setup: function(){ sfx = $('#qunit-fixture'), fx = $('#async-fixture'); - $.fn.editable.defaults.name = 'name2'; - //clear cache - $(document).removeData('groups.php-'+$.fn.editable.defaults.name); $.support.transition = false; } }); @@ -58,7 +55,7 @@ $(function () { }, timeout); }); - asyncTest("test limit", function () { + asyncTest("limit option", function () { var e = $('<a href="#" data-type="checklist" data-value="2,3" data-url="post.php"></a>').appendTo(fx).editable({ pk: 1, source: groupsArr, diff --git a/test/unit/date.js b/test/unit/date.js index e2ab4cc..83f6707 100644 --- a/test/unit/date.js +++ b/test/unit/date.js @@ -5,7 +5,6 @@ $(function () { module("date", { setup: function(){ fx = $('#async-fixture'); - $.fn.editable.defaults.name = 'name1'; dpg = $.fn.datepicker.DPGlobal; } }); diff --git a/test/unit/dateui.js b/test/unit/dateui.js index 91c22fb..f6a1d20 100644 --- a/test/unit/dateui.js +++ b/test/unit/dateui.js @@ -5,7 +5,6 @@ $(function () { module("dateui", { setup: function(){ fx = $('#async-fixture'); - $.fn.editable.defaults.name = 'name1'; } }); diff --git a/test/unit/select.js b/test/unit/select.js index fec4a8e..b4b969f 100644 --- a/test/unit/select.js +++ b/test/unit/select.js @@ -4,9 +4,6 @@ $(function () { setup: function(){ sfx = $('#qunit-fixture'), fx = $('#async-fixture'); - $.fn.editable.defaults.name = 'name1'; - //clear cache - $(document).removeData('groups.php-'+$.fn.editable.defaults.name); $.support.transition = false; } }); @@ -167,7 +164,7 @@ $(function () { }, timeout); }) - asyncTest("popover should save new selected value", function () { + asyncTest("should save new selected value", function () { var e = $('<a href="#" data-type="select" data-value="2" data-url="post.php">customer</a>').appendTo(fx).editable({ pk: 1, source: groups @@ -192,8 +189,8 @@ $(function () { e.remove(); start(); }, timeout); - }); - + }); + asyncTest("if new text is empty --> show emptytext on save", function () { var e = $('<a href="#" data-type="select" data-value="2" data-url="post.php">customer</a>').appendTo(fx).editable({ pk: 1, @@ -248,8 +245,11 @@ $(function () { }); asyncTest("cache request for same selects", function () { - var e = $('<a href="#" data-type="select" data-pk="1" data-value="2" data-url="post.php" data-source="groups-cache.php">customer</a>').appendTo(fx).editable(), - e1 = $('<a href="#" data-type="select" data-pk="1" data-value="2" data-url="post.php" data-source="groups-cache.php">customer</a>').appendTo(fx).editable(), + //clear cache + $(document).removeData('groups.php-name1'); + + var e = $('<a href="#" data-type="select" data-pk="1" data-name="name1" data-value="2" data-url="post.php" data-source="groups-cache.php">customer</a>').appendTo(fx).editable(), + e1 = $('<a href="#" data-type="select" data-pk="1" id="name1" data-value="2" data-url="post.php" data-source="groups-cache.php">customer</a>').appendTo(fx).editable(), req = 0; $.mockjax({ @@ -293,6 +293,10 @@ $(function () { asyncTest("cache simultaneous requests", function () { expect(4); + + //clear cache + $(document).removeData('groups.php-name1'); + var req = 0; $.mockjax({ url: 'groups-cache-sim.php', @@ -303,9 +307,9 @@ $(function () { } }); - var e = $('<a href="#" data-type="select" data-pk="1" data-value="1" data-url="post.php" data-source="groups-cache-sim.php"></a>').appendTo(fx).editable(), - e1 = $('<a href="#" data-type="select" data-pk="1" data-value="2" data-url="post.php" data-source="groups-cache-sim.php"></a>').appendTo(fx).editable(), - e2 = $('<a href="#" data-type="select" data-pk="1" data-value="3" data-url="post.php" data-source="groups-cache-sim.php"></a>').appendTo(fx).editable(); + var e = $('<a href="#" data-type="select" data-pk="1" data-name="name1" data-value="1" data-url="post.php" data-source="groups-cache-sim.php"></a>').appendTo(fx).editable(), + e1 = $('<a href="#" data-type="select" data-pk="1" data-name="name1" data-value="2" data-url="post.php" data-source="groups-cache-sim.php"></a>').appendTo(fx).editable(), + e2 = $('<a href="#" data-type="select" data-pk="1" data-name="name1" data-value="3" data-url="post.php" data-source="groups-cache-sim.php"></a>').appendTo(fx).editable(); setTimeout(function() { @@ -324,6 +328,10 @@ $(function () { asyncTest("cache simultaneous requests (loading error)", function () { expect(4); + + //clear cache + $(document).removeData('groups.php-name1'); + var req = 0; $.mockjax({ url: 'groups-cache-sim-err.php', @@ -334,9 +342,9 @@ $(function () { } }); - var e = $('<a href="#" data-type="select" data-pk="1" data-value="1" data-autotext="always" data-url="post.php" data-source="groups-cache-sim-err.php">35</a>').appendTo(fx).editable(), - e1 = $('<a href="#" data-type="select" data-pk="1" data-value="2" data-autotext="always" data-url="post.php" data-source="groups-cache-sim-err.php">35</a>').appendTo(fx).editable(), - e2 = $('<a href="#" data-type="select" data-pk="1" data-value="3" data-autotext="always" data-url="post.php" data-source="groups-cache-sim-err.php">6456</a>').appendTo(fx).editable(), + var e = $('<a href="#" data-type="select" data-pk="1" data-name="name1" data-value="1" data-autotext="always" data-url="post.php" data-source="groups-cache-sim-err.php">35</a>').appendTo(fx).editable(), + e1 = $('<a href="#" data-type="select" data-pk="1" data-name="name1" data-value="2" data-autotext="always" data-url="post.php" data-source="groups-cache-sim-err.php">35</a>').appendTo(fx).editable(), + e2 = $('<a href="#" data-type="select" data-pk="1" data-name="name1" data-value="3" data-autotext="always" data-url="post.php" data-source="groups-cache-sim-err.php">6456</a>').appendTo(fx).editable(), errText = $.fn.editableform.types.select.defaults.sourceError; setTimeout(function() { diff --git a/test/unit/text.js b/test/unit/text.js index df70bda..79f1b90 100644 --- a/test/unit/text.js +++ b/test/unit/text.js @@ -3,7 +3,6 @@ $(function () { module("text", { setup: function() { fx = $('#async-fixture'); - $.fn.editable.defaults.name = 'name1'; $.support.transition = false; } }); diff --git a/test/unit/textarea.js b/test/unit/textarea.js index cee65bc..6a76813 100644 --- a/test/unit/textarea.js +++ b/test/unit/textarea.js @@ -5,8 +5,8 @@ $(function () { module("textarea", { setup: function(){ - fx = $('#async-fixture'), - $.fn.editable.defaults.name = 'name1'; + fx = $('#async-fixture'); + $.support.transition = false; } }); From 396775bf8cd0e1200f122a27a50e5d8a6cbe9bb5 Mon Sep 17 00:00:00 2001 From: vitalets <noginsk@rambler.ru> Date: Tue, 27 Nov 2012 00:37:15 +0400 Subject: [PATCH 14/24] object.keys workaround --- src/editable-form/editable-form-utils.js | 196 ++++++++++++----------- src/inputs/select.js | 2 +- 2 files changed, 107 insertions(+), 91 deletions(-) diff --git a/src/editable-form/editable-form-utils.js b/src/editable-form/editable-form-utils.js index 3e2eaa6..4d8169d 100644 --- a/src/editable-form/editable-form-utils.js +++ b/src/editable-form/editable-form-utils.js @@ -2,104 +2,120 @@ * EditableForm utilites */ (function ($) { - $.extend($.fn.editableform, { - utils: { - /** - * 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; - }, + $.fn.editableform.utils = { + /** + * 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(); - } - }, + /** + * 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 { + /** + * 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; - }, + } + 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]; - } - } + /** + * slice object by specified keys + */ + sliceObj: function(obj, keys, caseSensitive /* default: false */) { + var key, keyLower, newObj = {}; + if (!$.isArray(keys) || !keys.length) { 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)) { - data[k] = v; - } - }); - return data; - } - } - }); + } + + 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)) { + data[k] = v; + } + }); + return data; + }, + + 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; + } + + } + }; }(window.jQuery)); \ No newline at end of file diff --git a/src/inputs/select.js b/src/inputs/select.js index 89b4265..9497ae1 100644 --- a/src/inputs/select.js +++ b/src/inputs/select.js @@ -1,5 +1,5 @@ /** -Select (dropdown) input +Select (dropdown) @class select @extends list From 1e757b833fe086a2722bbec70680cc7d32d12f5d Mon Sep 17 00:00:00 2001 From: vitalets <noginsk@rambler.ru> Date: Tue, 27 Nov 2012 00:37:57 +0400 Subject: [PATCH 15/24] css workaround to nowrap buttons in position absolute mode --- src/containers/editable-container.css | 10 +++++---- src/editable-form/editable-form.css | 30 +++++++++++++++------------ src/editable-form/editable-form.js | 2 +- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/containers/editable-container.css b/src/containers/editable-container.css index 927ab10..9098275 100644 --- a/src/containers/editable-container.css +++ b/src/containers/editable-container.css @@ -1,6 +1,8 @@ -.editable-container, -.editable-container.popover { - width: auto; - white-space: nowrap; /* without this rule buttons wrap input in non-static elements */ +.editable-container { max-width: none; /* without this rule poshytip does not stretch */ +} + +.editable-container.popover { +/* width: 300px;*/ /* debug */ + width: auto; /* without this rule popover does not stretch */ } \ No newline at end of file diff --git a/src/editable-form/editable-form.css b/src/editable-form/editable-form.css index 6c18918..446e3fa 100644 --- a/src/editable-form/editable-form.css +++ b/src/editable-form/editable-form.css @@ -1,11 +1,20 @@ -.editableform, -.editableform div.control-group { - margin-bottom: 0; - overflow: auto; +.editableform { + margin-bottom: 0; /* overwrites bootstrap margin */ +} + +.editableform .control-group { + margin-bottom: 0; /* overwrites bootstrap margin */ + white-space: nowrap; /* prevent wrapping buttons on new line */ } .editable-buttons { - float: left; + display: inline-block; /* should be inline to take effect of parent's white-space: nowrap */ +} + +.editable-input { + vertical-align: top; + display: inline-block; /* should be inline to take effect of parent's white-space: nowrap */ + width: auto; /* bootstrap-responsive has width: 100% that breakes layout */ } .editable-buttons button { @@ -28,6 +37,7 @@ .editable-error-block { max-width: 300px; + padding-top: 3px; margin: 0; clear: both; } @@ -36,14 +46,6 @@ color: red; } -.editable-input { - vertical-align: top; - display: inline-block; - width: auto; /* bootstrap-responsive has width: 100% that breakes layout */ - float: left; - padding-bottom: 9px; -} - .editableform textarea { height: 150px; /*default height for textarea*/ } @@ -51,9 +53,11 @@ .editableform .editable-date { padding: 0; margin: 0; + float: left; } .editable-clear { + clear: both; float: right; font-size: 0.9em; text-decoration: none; diff --git a/src/editable-form/editable-form.js b/src/editable-form/editable-form.js index 854e40d..8cf8bd2 100644 --- a/src/editable-form/editable-form.js +++ b/src/editable-form/editable-form.js @@ -23,7 +23,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. //create input of specified type if(typeof $.fn.editableform.types[this.options.type] === 'function') { TypeConstructor = $.fn.editableform.types[this.options.type]; - typeOptions = $.fn.editableform.utils.sliceObj(this.options, Object.keys(TypeConstructor.defaults)); + typeOptions = $.fn.editableform.utils.sliceObj(this.options, $.fn.editableform.utils.objectKeys(TypeConstructor.defaults)); this.input = new TypeConstructor(typeOptions); } else { $.error('Unknown type: '+ this.options.type); From 18f29820f3f6cb4936143161f3eac51e8557eb4d Mon Sep 17 00:00:00 2001 From: vitalets <noginsk@rambler.ru> Date: Tue, 27 Nov 2012 13:25:42 +0400 Subject: [PATCH 16/24] many fixes for ie7+ compatibility --- src/editable-form/editable-form-bootstrap.js | 4 +- src/editable-form/editable-form-jqueryui.js | 6 +- src/editable-form/editable-form.css | 20 +- src/editable-form/editable-form.js | 247 ++++++++++--------- src/element/editable-element.js | 8 +- src/inputs/date/date.js | 2 +- src/inputs/dateui/dateui.js | 2 +- 7 files changed, 154 insertions(+), 135 deletions(-) diff --git a/src/editable-form/editable-form-bootstrap.js b/src/editable-form/editable-form-bootstrap.js index f6a5273..d55e3fd 100644 --- a/src/editable-form/editable-form-bootstrap.js +++ b/src/editable-form/editable-form-bootstrap.js @@ -14,8 +14,8 @@ Editableform based on Twitter Bootstrap }); //buttons - $.fn.editableform.buttons = '<button type="submit" class="btn btn-primary"><i class="icon-ok icon-white"></i></button>'+ - '<button type="button" class="btn clearfix"><i class="icon-ban-circle"></i></button>'; + $.fn.editableform.buttons = '<button type="submit" class="btn btn-primary editable-submit"><i class="icon-ok icon-white"></i></button>'+ + '<button type="button" class="btn editable-cancel"><i class="icon-ban-circle"></i></button>'; //error classes $.fn.editableform.errorGroupClass = 'error'; diff --git a/src/editable-form/editable-form-jqueryui.js b/src/editable-form/editable-form-jqueryui.js index c0b6683..b5983c8 100644 --- a/src/editable-form/editable-form-jqueryui.js +++ b/src/editable-form/editable-form-jqueryui.js @@ -8,12 +8,12 @@ Editableform based on jQuery UI this.$form = $($.fn.editableform.template); //buttons - this.$form.find('div.editable-buttons').append($.fn.editableform.buttons); - this.$form.find('button[type=submit]').button({ + this.$form.find('.editable-buttons').append($.fn.editableform.buttons); + this.$form.find('.editable-submit').button({ icons: { primary: "ui-icon-check" }, text: false }).removeAttr('title'); - this.$form.find('button[type=button]').button({ + this.$form.find('.editable-cancel').button({ icons: { primary: "ui-icon-cancel" }, text: false }).removeAttr('title'); diff --git a/src/editable-form/editable-form.css b/src/editable-form/editable-form.css index 446e3fa..f7acde3 100644 --- a/src/editable-form/editable-form.css +++ b/src/editable-form/editable-form.css @@ -9,15 +9,24 @@ .editable-buttons { display: inline-block; /* should be inline to take effect of parent's white-space: nowrap */ + vertical-align: top; + margin-left: 7px; + /* display-inline emulation for IE7*/ + zoom: 1; + *display: inline; } .editable-input { vertical-align: top; display: inline-block; /* should be inline to take effect of parent's white-space: nowrap */ width: auto; /* bootstrap-responsive has width: 100% that breakes layout */ + white-space: normal; /* reset white-space decalred in parent*/ + /* display-inline emulation for IE7*/ + zoom: 1; + *display: inline; } -.editable-buttons button { +.editable-buttons .editable-cancel { margin-left: 7px; } @@ -28,7 +37,8 @@ .editableform-loading { background: url('img/loading.gif') center center no-repeat; - height: 25px; + height: 25px; + width: auto; } .editable-inline .editableform-loading { @@ -39,7 +49,7 @@ max-width: 300px; padding-top: 3px; margin: 0; - clear: both; + width: auto; } .editable-error { @@ -57,8 +67,8 @@ } .editable-clear { - clear: both; - float: right; + clear: both; font-size: 0.9em; text-decoration: none; + text-align: right; } \ No newline at end of file diff --git a/src/editable-form/editable-form.js b/src/editable-form/editable-form.js index 8cf8bd2..ed0ac53 100644 --- a/src/editable-form/editable-form.js +++ b/src/editable-form/editable-form.js @@ -8,7 +8,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. @uses textarea **/ (function ($) { - + var EditableForm = function (element, options) { this.options = $.extend({}, $.fn.editableform.defaults, options); this.$element = $(element); //div (usually), containing form. not form tag! @@ -19,7 +19,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. constructor: EditableForm, initInput: function() { //called once var TypeConstructor, typeOptions; - + //create input of specified type if(typeof $.fn.editableform.types[this.options.type] === 'function') { TypeConstructor = $.fn.editableform.types[this.options.type]; @@ -34,7 +34,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. }, initTemplate: function() { this.$form = $($.fn.editableform.template); - + //buttons this.$form.find('div.editable-buttons').append($.fn.editableform.buttons); }, @@ -47,54 +47,57 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. this.$loading = $($.fn.editableform.loading); this.$element.empty().append(this.$loading); this.showLoading(); - + this.initTemplate(); - + /** Fired when rendering starts @event rendering @param {Object} event event object **/ this.$element.triggerHandler('rendering'); - + //render input $.when(this.input.render()) .then($.proxy(function () { //input this.$form.find('div.editable-input').append(this.input.$input); - - //clear link + + //"clear" link if(this.input.$clear) { - this.$form.find('div.editable-input').append(this.input.$clear); + this.$form.find('div.editable-input').append($('<div class="editable-clear">').append(this.input.$clear)); } - - //attach 'cancel' handler - this.$form.find('button[type=button]').click($.proxy(this.cancel, this)); + //append form to container this.$element.append(this.$form); + + //attach 'cancel' handler + this.$form.find('.editable-cancel').click($.proxy(this.cancel, this)); + // this.$form.find('.editable-buttons button').eq(1).click($.proxy(this.cancel, this)); + if(this.input.error) { this.error(this.input.error); - this.$form.find('button[type=submit]').attr('disabled', true); + this.$form.find('.editable-submit').attr('disabled', true); this.input.$input.attr('disabled', true); } else { this.error(false); this.input.$input.removeAttr('disabled'); - this.$form.find('button[type=submit]').removeAttr('disabled'); + this.$form.find('.editable-submit').removeAttr('disabled'); this.input.value2input(this.value); this.$form.submit($.proxy(this.submit, this)); } - + /** Fired when form is rendered @event rendered @param {Object} event event object **/ this.$element.triggerHandler('rendered'); - + this.showForm(); }, this)); }, - cancel: function() { + cancel: function() { /** Fired when form was cancelled by user @event cancel @@ -103,12 +106,18 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. this.$element.triggerHandler('cancel'); }, showLoading: function() { - var fw, fh, iw, bh; - //set loading size equal to form + var w; if(this.$form) { + //set loading size equal to form this.$loading.width(this.$form.outerWidth()); this.$loading.height(this.$form.outerHeight()); this.$form.hide(); + } else { + //stretch loading to fill container width + w = this.$loading.parent().width(); + if(w) { + this.$loading.width(w); + } } this.$loading.show(); }, @@ -124,11 +133,11 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. **/ this.$element.triggerHandler('show'); }, - + error: function(msg) { var $group = this.$form.find('.control-group'), - $block = this.$form.find('.editable-error-block'); - + $block = this.$form.find('.editable-error-block'); + if(msg === false) { $group.removeClass($.fn.editableform.errorGroupClass); $block.removeClass($.fn.editableform.errorBlockClass).empty().hide(); @@ -137,15 +146,15 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. $block.addClass($.fn.editableform.errorBlockClass).text(msg).show(); } }, - + submit: function(e) { e.stopPropagation(); e.preventDefault(); var error, - //get value from input - newValue = this.input.input2value(), - newValueStr; + //get value from input + newValue = this.input.input2value(), + newValueStr; //validation if (error = this.validate(newValue)) { @@ -153,14 +162,14 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. this.showForm(); return; } - + //value as string newValueStr = this.input.value2str(newValue); - + //if value not changed --> cancel /*jslint eqeq: true*/ if (newValueStr == this.input.value2str(this.value)) { - /*jslint eqeq: false*/ + /*jslint eqeq: false*/ this.cancel(); return; } @@ -175,36 +184,36 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. this.showForm(); return; } - - //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 submitted value - @param {Object} params.response ajax response - - @example - $('#form-div').on('save'), function(e, params){ - if(params.newValue === 'username') {...} - }); - **/ - this.$element.triggerHandler('save', {newValue: newValue, response: response}); + + //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 submitted value + @param {Object} params.response ajax response + + @example + $('#form-div').on('save'), function(e, params){ + if(params.newValue === 'username') {...} + }); + **/ + this.$element.triggerHandler('save', {newValue: newValue, response: response}); }, this)) .fail($.proxy(function(xhr) { - this.error(typeof xhr === 'string' ? xhr : xhr.responseText || xhr.statusText || 'Unknown error!'); - this.showForm(); + this.error(typeof xhr === 'string' ? xhr : xhr.responseText || xhr.statusText || 'Unknown error!'); + this.showForm(); }, this)); }, save: function(value) { var pk = (typeof this.options.pk === 'function') ? this.options.pk.call(this) : this.options.pk, - send = !!(typeof this.options.url === 'function' || (this.options.url && ((this.options.send === 'always') || (this.options.send === 'auto' && pk)))), - params; - + send = !!(typeof this.options.url === 'function' || (this.options.url && ((this.options.send === 'always') || (this.options.send === 'auto' && pk)))), + params; + if (send) { //send to server this.showLoading(); @@ -214,7 +223,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. value: value, pk: pk }; - + //additional params if(typeof this.options.params === 'function') { $.extend(params, this.options.params.call(this, params)); @@ -236,7 +245,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. } } }, - + validate: function (value) { if (value === undefined) { value = this.value; @@ -245,38 +254,38 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. return this.options.validate.call(this, value); } }, - - option: function(key, value) { - this.options[key] = value; - if(key === 'value') { - this.setValue(value); - } - }, - - setValue: function(value, convertStr) { - if(convertStr) { - this.value = this.input.str2value(value); - } else { - this.value = value; - } - } + + option: function(key, value) { + this.options[key] = value; + if(key === 'value') { + this.setValue(value); + } + }, + + setValue: function(value, convertStr) { + if(convertStr) { + this.value = this.input.str2value(value); + } else { + this.value = value; + } + } }; /* Initialize editableform. Applied to jQuery object. - + @method $().editableform(options) @params {Object} options @example - var $form = $('<div>').editableform({ - type: 'text', - name: 'username', - url: '/post', - value: 'vitaliy' - }); - - //to display form you should call 'render' method - $form.editableform('render'); + var $form = $('<div>').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; @@ -287,20 +296,20 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. 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> @@ -318,12 +327,12 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. @default null @example url: function(params) { - if(params.value === 'abc') { - var d = new $.Deferred; - return d.reject('field cannot be "abc"'); //returning error via deferred object - } else { - someModel.set(params.name, params.value); //save data in some js model - } + if(params.value === 'abc') { + var d = new $.Deferred; + return d.reject('field cannot be "abc"'); //returning error via deferred object + } else { + someModel.set(params.name, params.value); //save data in some js model + } } **/ url:null, @@ -331,7 +340,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. Additional params for submit. Function can be used to calculate params dynamically @example params: function() { - return { a: 1 }; + return { a: 1 }; } @property params @@ -382,54 +391,54 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. @default null @example validate: function(value) { - if($.trim(value) == '') { - return 'This field is required'; - } + if($.trim(value) == '') { + return 'This field is required'; + } } **/ validate: null, /** Success callback. Called when value successfully sent on server and response status = 200. Can be used to process json response. If this function returns string - means error occured and string is shown as error message. - + @property success @type function @default null @example success: function(response, newValue) { - if(!response.success) return response.msg; + if(!response.success) return response.msg; } **/ success: function(response, newValue) {} }; /* - Note: following params could redefined in engine: bootstrap or jqueryui: - Classes 'control-group' and 'editable-error-block' must always present! + 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 class="editable-input"></div><div class="editable-buttons"></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">ok</button>'+ - '<button type="button">cancel</button>'; - - //error class attahced to control-group - $.fn.editableform.errorGroupClass = null; - - //error class attahced to editable-error-block - $.fn.editableform.errorBlockClass = 'editable-error'; + $.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>'; - //input types - $.fn.editableform.types = {}; - //utils - $.fn.editableform.utils = {}; + //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 attahced to control-group + $.fn.editableform.errorGroupClass = null; + + //error class attahced to editable-error-block + $.fn.editableform.errorBlockClass = 'editable-error'; + + //input types + $.fn.editableform.types = {}; + //utils + $.fn.editableform.utils = {}; }(window.jQuery)); \ No newline at end of file diff --git a/src/element/editable-element.js b/src/element/editable-element.js index fe581cd..f51afec 100644 --- a/src/element/editable-element.js +++ b/src/element/editable-element.js @@ -35,7 +35,7 @@ Makes editable any HTML element on the page. Applied as jQuery method. //create input of specified type. Input will be used for converting value, not in form if(typeof $.fn.editableform.types[this.options.type] === 'function') { TypeConstructor = $.fn.editableform.types[this.options.type]; - this.typeOptions = $.fn.editableform.utils.sliceObj(this.options, Object.keys(TypeConstructor.defaults)); + this.typeOptions = $.fn.editableform.utils.sliceObj(this.options, $.fn.editableform.utils.objectKeys(TypeConstructor.defaults)); this.input = new TypeConstructor(this.typeOptions); } else { $.error('Unknown type: '+ this.options.type); @@ -53,7 +53,7 @@ Makes editable any HTML element on the page. Applied as jQuery method. //attach handler to close any container on escape $(document).off('keyup.editable').on('keyup.editable', function (e) { if (e.which === 27) { - $('.editable-container').find('button[type=button]').click(); + $('.editable-container').find('.editable-cancel').click(); } }); @@ -65,7 +65,7 @@ Makes editable any HTML element on the page. Applied as jQuery method. return; } //close all other containers - $('.editable-container').find('button[type=button]').click(); + $('.editable-container').find('.editable-cancel').click(); }); //add 'editable' class @@ -229,7 +229,7 @@ Makes editable any HTML element on the page. Applied as jQuery method. } //hide all other editable containers. Required to work correctly with toggle = manual - $('.editable-container').find('button[type=button]').click(); + $('.editable-container').find('.editable-cancel').click(); //show container this.container.show(); diff --git a/src/inputs/date/date.js b/src/inputs/date/date.js index b16f57e..6490021 100644 --- a/src/inputs/date/date.js +++ b/src/inputs/date/date.js @@ -56,7 +56,7 @@ $(function(){ this.$input.datepicker(this.options.datepicker); if(this.options.clear) { - this.$clear = $('<a href="#">').addClass('editable-clear').html(this.options.clear).click($.proxy(function(e){ + this.$clear = $('<a href="#"></a>').html(this.options.clear).click($.proxy(function(e){ e.preventDefault(); e.stopPropagation(); this.clear(); diff --git a/src/inputs/dateui/dateui.js b/src/inputs/dateui/dateui.js index 986726a..5b39f27 100644 --- a/src/inputs/dateui/dateui.js +++ b/src/inputs/dateui/dateui.js @@ -54,7 +54,7 @@ $(function(){ this.$input.datepicker(this.options.datepicker); if(this.options.clear) { - this.$clear = $('<a href="#">').addClass('editable-clear').html(this.options.clear).click($.proxy(function(e){ + this.$clear = $('<a href="#"></a>').html(this.options.clear).click($.proxy(function(e){ e.preventDefault(); e.stopPropagation(); this.clear(); From 02c4c685ae915326a63754c4dc3f88b7f5fc7149 Mon Sep 17 00:00:00 2001 From: vitalets <noginsk@rambler.ru> Date: Tue, 27 Nov 2012 13:47:29 +0400 Subject: [PATCH 17/24] fix tests for abs position and date clear --- test/unit/common.js | 12 +++++++++++- test/unit/date.js | 2 +- test/unit/dateui.js | 4 ++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/test/unit/common.js b/test/unit/common.js index c6fd49e..468c48e 100644 --- a/test/unit/common.js +++ b/test/unit/common.js @@ -122,7 +122,17 @@ ok(!p.is(':visible'), 'popover closed'); }); - + test("should not wrap buttons when parent has position:absolute", function () { + var d = $('<div style="position: absolute; top: 200px">').appendTo(fx), + e = $('<a href="#" data-pk="1" data-url="post.php" data-name="text1">abc</a>').appendTo(d).editable(); + + e.click(); + var p = tip(e); + ok(p.find('button').offset().top < p.find('.editable-input').offset().top + p.find('.editable-input').height(), 'buttons top ok'); + ok(p.find('button').offset().left > p.find('.editable-input').offset().left + p.find('.editable-input').width(), 'buttons left ok'); + + d.remove(); + }); //unfortunatly, testing this feature does not always work in browsers. Tested manually. /* diff --git a/test/unit/date.js b/test/unit/date.js index 83f6707..16ca25a 100644 --- a/test/unit/date.js +++ b/test/unit/date.js @@ -153,7 +153,7 @@ $(function () { equal(frmt(e.data('editable').value, f), d, 'day set correct'); equal(p.find('td.day.active').text(), 15, 'day shown correct'); - var clear = p.find('.editable-clear'); + var clear = p.find('.editable-clear a'); equal(clear.text(), 'abc', 'clear link shown'); //click clear diff --git a/test/unit/dateui.js b/test/unit/dateui.js index f6a1d20..5af7255 100644 --- a/test/unit/dateui.js +++ b/test/unit/dateui.js @@ -105,7 +105,7 @@ $(function () { equal(frmt(e.data('editable').value, f), d, 'day set correct'); equal(p.find('a.ui-state-active').text(), 15, 'day shown correct'); - var clear = p.find('.editable-clear'); + var clear = p.find('.editable-clear a'); equal(clear.text(), 'abc', 'clear link shown'); //click clear @@ -118,7 +118,7 @@ $(function () { equal(e.text(), e.data('editable').options.emptytext, 'empty text shown'); e.remove(); start(); - }, timeout); + }, 500); }); From 5ef1a9e18465f77d46b712a2ac935bce4622ec6f Mon Sep 17 00:00:00 2001 From: vitalets <noginsk@rambler.ru> Date: Tue, 27 Nov 2012 14:09:48 +0400 Subject: [PATCH 18/24] chcklist vertical alignment --- src/editable-form/editable-form.css | 8 ++++++++ src/inputs/checklist.js | 13 ++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/editable-form/editable-form.css b/src/editable-form/editable-form.css index f7acde3..049eb56 100644 --- a/src/editable-form/editable-form.css +++ b/src/editable-form/editable-form.css @@ -66,6 +66,14 @@ float: left; } + +/* checklist vertical alignment */ +.editable-checklist label input[type="checkbox"], +.editable-checklist label span { + vertical-align: middle; + margin: 0; +} + .editable-clear { clear: both; font-size: 0.9em; diff --git a/src/inputs/checklist.js b/src/inputs/checklist.js index 4bd634a..ffdc7c9 100644 --- a/src/inputs/checklist.js +++ b/src/inputs/checklist.js @@ -36,12 +36,12 @@ $(function(){ } for(var i=0; i<this.sourceData.length; i++) { - $label = $('<label>').text(' '+this.sourceData[i].text) - .prepend($('<input>', { + $label = $('<label>').append($('<input>', { type: 'checkbox', value: this.sourceData[i].value, name: this.options.name - })); + })) + .append($('<span>').text(' '+this.sourceData[i].text)); $('<div>').append($label).appendTo(this.$input); } @@ -109,6 +109,13 @@ $(function(){ **/ tpl:'<div></div>', + /** + @property inputclass + @type string + @default span2 editable-checklist + **/ + inputclass: 'span2 editable-checklist', + /** Separator of values in string when sending to server From 5d8861a49ab5743c8e84aab920383d0b7c5df046 Mon Sep 17 00:00:00 2001 From: vitalets <noginsk@rambler.ru> Date: Tue, 27 Nov 2012 14:21:08 +0400 Subject: [PATCH 19/24] change cancel button --- CHANGELOG.txt | 1 + README.md | 2 +- src/editable-form/editable-form-bootstrap.js | 2 +- src/editable-form/editable-form-jqueryui.js | 2 +- src/editable-form/editable-form.css | 8 ++++++-- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 07824fc..6b1e46b 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -3,6 +3,7 @@ X-editable changelog Version 1.1.0 wip ---------------------------- +[enh] added support for IE7+ (vitalets) [enh #9] 'name' or 'id' is not required anymore (vitalets) [enh] 'clear' button added in date and dateui (vitalets) [enh] form template changed: added DIV.editable-input, DIV.editable.buttons and $.fn.editableform buttons (vitalets) diff --git a/README.md b/README.md index d8fe40a..1a3ef1a 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Your feedback is very appreciated! ## Contribution A few steps how to start contributing: -1.[Fork X-editable](https://github.com/vitalets/x-editable/fork) +1.[Fork X-editable](https://github.com/vitalets/x-editable/fork) and pull the latest changes from <code>dev</code> branch 2.Arrange local directory structure. It should be: **x-editable** diff --git a/src/editable-form/editable-form-bootstrap.js b/src/editable-form/editable-form-bootstrap.js index d55e3fd..50f72bb 100644 --- a/src/editable-form/editable-form-bootstrap.js +++ b/src/editable-form/editable-form-bootstrap.js @@ -15,7 +15,7 @@ Editableform based on Twitter Bootstrap //buttons $.fn.editableform.buttons = '<button type="submit" class="btn btn-primary editable-submit"><i class="icon-ok icon-white"></i></button>'+ - '<button type="button" class="btn editable-cancel"><i class="icon-ban-circle"></i></button>'; + '<button type="button" class="btn editable-cancel"><i class="icon-remove"></i></button>'; //error classes $.fn.editableform.errorGroupClass = 'error'; diff --git a/src/editable-form/editable-form-jqueryui.js b/src/editable-form/editable-form-jqueryui.js index b5983c8..804b6d2 100644 --- a/src/editable-form/editable-form-jqueryui.js +++ b/src/editable-form/editable-form-jqueryui.js @@ -14,7 +14,7 @@ Editableform based on jQuery UI text: false }).removeAttr('title'); this.$form.find('.editable-cancel').button({ - icons: { primary: "ui-icon-cancel" }, + icons: { primary: "ui-icon-closethick" }, text: false }).removeAttr('title'); diff --git a/src/editable-form/editable-form.css b/src/editable-form/editable-form.css index 049eb56..1ff18fe 100644 --- a/src/editable-form/editable-form.css +++ b/src/editable-form/editable-form.css @@ -47,11 +47,15 @@ .editable-error-block { max-width: 300px; - padding-top: 3px; - margin: 0; + margin: 5px 0 0 0; width: auto; } +/*add padding for jquery ui*/ +.editable-error-block.ui-state-error { + padding: 3px; +} + .editable-error { color: red; } From f50bb8d4cb1b1537ddde5f1d86e2c1be6e6fb538 Mon Sep 17 00:00:00 2001 From: vitalets <noginsk@rambler.ru> Date: Tue, 27 Nov 2012 14:27:10 +0400 Subject: [PATCH 20/24] tiny fixes --- CHANGELOG.txt | 1 + README.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 6b1e46b..dd7da67 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -3,6 +3,7 @@ X-editable changelog Version 1.1.0 wip ---------------------------- +[enh #11] cancel button changed to 'cross' (tarciozemel) [enh] added support for IE7+ (vitalets) [enh #9] 'name' or 'id' is not required anymore (vitalets) [enh] 'clear' button added in date and dateui (vitalets) diff --git a/README.md b/README.md index 1a3ef1a..ed6d827 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ Or use grunt's _qunit_ task <code>grunt test</code>. For that you also need to [ 5.To build lib + docs: * run <code>grunt build</code> in **lib** directory * run <code>build data-docs-dist</code> in **gh-pages** directory -You will get distributive in **lib/dist** and updated docs in **gh-pages/*.html**. +You will get distributive in **lib/dist** and updated docs in **gh-pages/index.html** and **gh-pages/docs.html** (don't edit these files directly). 6.Commit changes on <code>dev</code> branch and make pull request as usual. From d3ccad1c2b86f7925ad4583e0e4e660a9511e09c Mon Sep 17 00:00:00 2001 From: vitalets <noginsk@rambler.ru> Date: Tue, 27 Nov 2012 15:38:12 +0400 Subject: [PATCH 21/24] readme updated --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ed6d827..ca3354d 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,8 @@ Or use grunt's _qunit_ task <code>grunt test</code>. For that you also need to [ 5.To build lib + docs: * run <code>grunt build</code> in **lib** directory * run <code>build data-docs-dist</code> in **gh-pages** directory -You will get distributive in **lib/dist** and updated docs in **gh-pages/index.html** and **gh-pages/docs.html** (don't edit these files directly). +You will get distributive in **lib/dist** and updated docs in **gh-pages/*.html**. +Do not edit **index.html** and **docs.html** directly! Instead look at [Handlebars](https://github.com/wycats/handlebars.js) templates in **generator/templates**. 6.Commit changes on <code>dev</code> branch and make pull request as usual. From 30f7c203604cdbc6ace450eab7de7e3f00b8d962 Mon Sep 17 00:00:00 2001 From: vitalets <noginsk@rambler.ru> Date: Tue, 27 Nov 2012 15:40:36 +0400 Subject: [PATCH 22/24] set version 1.1.0 --- CHANGELOG.txt | 7 ++++--- package.json | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index dd7da67..8257188 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,13 +1,14 @@ X-editable changelog ============================= -Version 1.1.0 wip + +Version 1.1.0 Nov 27, 2012 ---------------------------- -[enh #11] cancel button changed to 'cross' (tarciozemel) +[enh #11] icon cancel changed to 'cross' (tarciozemel) [enh] added support for IE7+ (vitalets) [enh #9] 'name' or 'id' is not required anymore (vitalets) [enh] 'clear' button added in date and dateui (vitalets) -[enh] form template changed: added DIV.editable-input, DIV.editable.buttons and $.fn.editableform buttons (vitalets) +[enh] form template changed: added DIV.editable-input, DIV.editable.buttons and $.fn.editableform.buttons (vitalets) [enh] new input type: checklist (vitalets) [enh] updated docs: inputs dropdown menu, global templates section (vitalets) diff --git a/package.json b/package.json index 40065b1..f7cf0e1 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "X-editable", "title": "X-editable", "description": "In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery", - "version": "1.0.1", + "version": "1.1.0", "homepage": "http://github.com/vitalets/x-editable", "author": { "name": "Vitaliy Potapov", From 4e616680fd1db77762ec438113f97abb10264fc0 Mon Sep 17 00:00:00 2001 From: vitalets <noginsk@rambler.ru> Date: Tue, 27 Nov 2012 15:46:17 +0400 Subject: [PATCH 23/24] fix indent in examples --- src/editable-form/editable-form.js | 34 +++++++++++++++--------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/editable-form/editable-form.js b/src/editable-form/editable-form.js index ed0ac53..b6f8018 100644 --- a/src/editable-form/editable-form.js +++ b/src/editable-form/editable-form.js @@ -198,7 +198,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. @example $('#form-div').on('save'), function(e, params){ - if(params.newValue === 'username') {...} + if(params.newValue === 'username') {...} }); **/ this.$element.triggerHandler('save', {newValue: newValue, response: response}); @@ -278,10 +278,10 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. @params {Object} options @example var $form = $('<div>').editableform({ - type: 'text', - name: 'username', - url: '/post', - value: 'vitaliy' + type: 'text', + name: 'username', + url: '/post', + value: 'vitaliy' }); //to display form you should call 'render' method @@ -327,20 +327,20 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. @default null @example url: function(params) { - if(params.value === 'abc') { - var d = new $.Deferred; - return d.reject('field cannot be "abc"'); //returning error via deferred object - } else { - someModel.set(params.name, params.value); //save data in some js model - } - } + if(params.value === 'abc') { + var d = new $.Deferred; + return d.reject('field cannot be "abc"'); //returning error via deferred object + } else { + someModel.set(params.name, params.value); //save data in some js model + } + } **/ url:null, /** Additional params for submit. Function can be used to calculate params dynamically @example params: function() { - return { a: 1 }; + return { a: 1 }; } @property params @@ -391,9 +391,9 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. @default null @example validate: function(value) { - if($.trim(value) == '') { - return 'This field is required'; - } + if($.trim(value) == '') { + return 'This field is required'; + } } **/ validate: null, @@ -406,7 +406,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'. @default null @example success: function(response, newValue) { - if(!response.success) return response.msg; + if(!response.success) return response.msg; } **/ success: function(response, newValue) {} From 54919a21775d6d6ef6dda647c0bd35c18f12cd83 Mon Sep 17 00:00:00 2001 From: vitalets <noginsk@rambler.ru> Date: Tue, 27 Nov 2012 15:51:31 +0400 Subject: [PATCH 24/24] list comments tiny fix --- src/inputs/checklist.js | 2 +- src/inputs/list.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/inputs/checklist.js b/src/inputs/checklist.js index ffdc7c9..1a5b149 100644 --- a/src/inputs/checklist.js +++ b/src/inputs/checklist.js @@ -143,7 +143,7 @@ $(function(){ limit: 4, /** Text shown when count of checked items is greater than <code>limit</code> parameter. - You can use <code>{checked}</code> and <code>count</code> placeholders. + You can use <code>{checked}</code> and <code>{count}</code> placeholders. @property limitText @type string diff --git a/src/inputs/list.js b/src/inputs/list.js index fc8412c..ad53e68 100644 --- a/src/inputs/list.js +++ b/src/inputs/list.js @@ -222,7 +222,7 @@ List - abstract class for inputs that have source option loaded from js array or List.defaults = $.extend({}, $.fn.editableform.types.abstract.defaults, { /** - Source data for dropdown list. If string - considered ajax url to load items. Otherwise should be an array. + Source data for list. If string - considered ajax url to load items. Otherwise should be an array. Array format is: <code>[{value: 1, text: "text"}, {...}]</code><br> For compability it also supports format <code>{value1: "text1", value2: "text2" ...}</code> but it does not guarantee elements order.