From 5c5a24b86bad0a9f17539409478fdf369fed528a Mon Sep 17 00:00:00 2001 From: vitalets <noginsk@rambler.ru> Date: Sat, 31 Aug 2013 09:53:34 +0400 Subject: [PATCH] bs3 first working version --- src/containers/editable-container.js | 5 +- src/containers/editable-popover.js | 5 +- src/containers/editable-popover3.js | 199 ++++++++++++++++++ src/containers/editable-poshytip.js | 1 + src/containers/editable-tooltip.js | 5 +- src/editable-form/editable-form-bootstrap.js | 24 ++- src/editable-form/editable-form-bootstrap3.js | 61 ++++++ src/editable-form/editable-form-jqueryui.js | 2 + src/editable-form/editable-form.js | 9 +- src/element/editable-element.js | 2 +- src/inputs/abstract.js | 6 +- src/inputs/combodate/combodate.js | 7 + src/inputs/select2/select2.js | 2 +- test/loader.js | 35 ++- 14 files changed, 339 insertions(+), 24 deletions(-) create mode 100644 src/containers/editable-popover3.js create mode 100644 src/editable-form/editable-form-bootstrap3.js diff --git a/src/containers/editable-container.js b/src/containers/editable-container.js index 0bfbe94..a106784 100644 --- a/src/containers/editable-container.js +++ b/src/containers/editable-container.js @@ -24,6 +24,8 @@ Applied as jQuery method. containerDataName: null, //object name in element's .data() innerCss: null, //tbd in child class containerClass: 'editable-container editable-popup', //css class applied to container element + defaults: {}, //container itself defaults + init: function(element, options) { this.$element = $(element); //since 1.4.1 container do not use data-* directly as they already merged into options. @@ -101,10 +103,9 @@ Applied as jQuery method. throw new Error(this.containerName + ' not found. Have you included corresponding js file?'); } - var cDef = $.fn[this.containerName].defaults; //keys defined in container defaults go to container, others go to form for(var k in this.options) { - if(k in cDef) { + if(k in this.defaults) { this.containerOptions[k] = this.options[k]; } else { this.formOptions[k] = this.options[k]; diff --git a/src/containers/editable-popover.js b/src/containers/editable-popover.js index 867f5bf..4740ac0 100644 --- a/src/containers/editable-popover.js +++ b/src/containers/editable-popover.js @@ -11,13 +11,14 @@ containerName: 'popover', //for compatibility with bootstrap <= 2.2.1 (content inserted into <p> instead of directly .popover-content) innerCss: $.fn.popover && $($.fn.popover.defaults.template).find('p').length ? '.popover-content p' : '.popover-content', - + defaults: $.fn.popover.defaults, + initContainer: function(){ $.extend(this.containerOptions, { trigger: 'manual', selector: false, content: ' ', - template: $.fn.popover.defaults.template + template: this.defaults.template }); //as template property is used in inputs, hide it from popover diff --git a/src/containers/editable-popover3.js b/src/containers/editable-popover3.js new file mode 100644 index 0000000..0ae0575 --- /dev/null +++ b/src/containers/editable-popover3.js @@ -0,0 +1,199 @@ +/** +* Editable Popover3 (for Bootstrap 3) +* --------------------- +* requires bootstrap-popover.js +*/ +(function ($) { + "use strict"; + + //extend methods + $.extend($.fn.editableContainer.Popup.prototype, { + containerName: 'popover', + containerDataName: 'bs.popover', + innerCss: '.popover-content', + defaults: $.fn.popover.Constructor.DEFAULTS, + + initContainer: function(){ + $.extend(this.containerOptions, { + trigger: 'manual', + selector: false, + content: ' ', + template: this.defaults.template + }); + + //as template property is used in inputs, hide it from popover + var t; + if(this.$element.data('template')) { + t = this.$element.data('template'); + this.$element.removeData('template'); + } + + this.call(this.containerOptions); + + if(t) { + //restore data('template') + this.$element.data('template', t); + } + }, + + /* show */ + innerShow: function () { + this.call('show'); + }, + + /* hide */ + innerHide: function () { + this.call('hide'); + }, + + /* destroy */ + innerDestroy: function() { + this.call('destroy'); + }, + + setContainerOption: function(key, value) { + this.container().options[key] = value; + }, + + /** + * move popover to new position. This function mainly copied from bootstrap-popover. + */ + /*jshint laxcomma: true*/ + setPosition: function () { + + (function() { + /* + var $tip = this.tip() + , inside + , pos + , actualWidth + , actualHeight + , placement + , tp + , tpt + , tpb + , tpl + , tpr; + + placement = typeof this.options.placement === 'function' ? + this.options.placement.call(this, $tip[0], this.$element[0]) : + this.options.placement; + + inside = /in/.test(placement); + + $tip + // .detach() + //vitalets: remove any placement class because otherwise they dont influence on re-positioning of visible popover + .removeClass('top right bottom left') + .css({ top: 0, left: 0, display: 'block' }); + // .insertAfter(this.$element); + + pos = this.getPosition(inside); + + actualWidth = $tip[0].offsetWidth; + actualHeight = $tip[0].offsetHeight; + + placement = inside ? placement.split(' ')[1] : placement; + + tpb = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}; + tpt = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}; + tpl = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}; + tpr = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}; + + switch (placement) { + case 'bottom': + if ((tpb.top + actualHeight) > ($(window).scrollTop() + $(window).height())) { + if (tpt.top > $(window).scrollTop()) { + placement = 'top'; + } else if ((tpr.left + actualWidth) < ($(window).scrollLeft() + $(window).width())) { + placement = 'right'; + } else if (tpl.left > $(window).scrollLeft()) { + placement = 'left'; + } else { + placement = 'right'; + } + } + break; + case 'top': + if (tpt.top < $(window).scrollTop()) { + if ((tpb.top + actualHeight) < ($(window).scrollTop() + $(window).height())) { + placement = 'bottom'; + } else if ((tpr.left + actualWidth) < ($(window).scrollLeft() + $(window).width())) { + placement = 'right'; + } else if (tpl.left > $(window).scrollLeft()) { + placement = 'left'; + } else { + placement = 'right'; + } + } + break; + case 'left': + if (tpl.left < $(window).scrollLeft()) { + if ((tpr.left + actualWidth) < ($(window).scrollLeft() + $(window).width())) { + placement = 'right'; + } else if (tpt.top > $(window).scrollTop()) { + placement = 'top'; + } else if (tpt.top > $(window).scrollTop()) { + placement = 'bottom'; + } else { + placement = 'right'; + } + } + break; + case 'right': + if ((tpr.left + actualWidth) > ($(window).scrollLeft() + $(window).width())) { + if (tpl.left > $(window).scrollLeft()) { + placement = 'left'; + } else if (tpt.top > $(window).scrollTop()) { + placement = 'top'; + } else if (tpt.top > $(window).scrollTop()) { + placement = 'bottom'; + } + } + break; + } + + switch (placement) { + case 'bottom': + tp = tpb; + break; + case 'top': + tp = tpt; + break; + case 'left': + tp = tpl; + break; + case 'right': + tp = tpr; + break; + } + + $tip + .offset(tp) + .addClass(placement) + .addClass('in'); + */ + + + var $tip = this.tip(); + + var placement = typeof this.options.placement == 'function' ? + this.options.placement.call(this, $tip[0], this.$element[0]) : + this.options.placement; + + + var pos = this.getPosition(); + var actualWidth = $tip[0].offsetWidth; + var actualHeight = $tip[0].offsetHeight; + var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight); + + this.applyPlacement(calculatedOffset, placement); + + + + }).call(this.container()); + /*jshint laxcomma: false*/ + } + }); + +}(window.jQuery)); diff --git a/src/containers/editable-poshytip.js b/src/containers/editable-poshytip.js index b797080..32a1d6f 100644 --- a/src/containers/editable-poshytip.js +++ b/src/containers/editable-poshytip.js @@ -10,6 +10,7 @@ $.extend($.fn.editableContainer.Popup.prototype, { containerName: 'poshytip', innerCss: 'div.tip-inner', + defaults: $.fn.poshytip.defaults, initContainer: function(){ this.handlePlacement(); diff --git a/src/containers/editable-tooltip.js b/src/containers/editable-tooltip.js index 0392727..8dd1cb9 100644 --- a/src/containers/editable-tooltip.js +++ b/src/containers/editable-tooltip.js @@ -12,6 +12,7 @@ //object name in element's .data() containerDataName: 'ui-tooltip', innerCss: '.ui-tooltip-content', + defaults: $.ui.tooltip.prototype.options, //split options on containerOptions and formOptions splitOptions: function() { @@ -23,10 +24,10 @@ $.error('Please use jQueryUI with "tooltip" widget! http://jqueryui.com/download'); return; } + //defaults for tooltip - var cDef = $.ui[this.containerName].prototype.options; for(var k in this.options) { - if(k in cDef) { + if(k in this.defaults) { this.containerOptions[k] = this.options[k]; } else { this.formOptions[k] = this.options[k]; diff --git a/src/editable-form/editable-form-bootstrap.js b/src/editable-form/editable-form-bootstrap.js index 8254279..cbcc044 100644 --- a/src/editable-form/editable-form-bootstrap.js +++ b/src/editable-form/editable-form-bootstrap.js @@ -1,13 +1,31 @@ /* -Editableform based on Twitter Bootstrap +Editableform based on Twitter Bootstrap 2 */ (function ($) { "use strict"; + //store parent methods + var pInitInput = $.fn.editableform.Constructor.prototype.initInput; + $.extend($.fn.editableform.Constructor.prototype, { initTemplate: function() { this.$form = $($.fn.editableform.template); this.$form.find('.editable-error-block').addClass('help-block'); + }, + initInput: function() { + pInitInput.apply(this); + + //for bs2 set default class `input-medium` to standard inputs + var emptyInputClass = this.input.options.inputclass === null || this.input.options.inputclass === false; + var defaultClass = 'input-medium'; + + //add bs2 default class to standard inputs + //if(this.input.$input.is('input,select,textarea')) { + var stdtypes = 'text,select,textarea,password,email,url,tel,number,range,time'.split(','); + if(~$.inArray(this.input.type, stdtypes) && emptyInputClass) { + this.input.options.inputclass = defaultClass; + this.input.$input.addClass(defaultClass); + } } }); @@ -17,6 +35,8 @@ Editableform based on Twitter Bootstrap //error classes $.fn.editableform.errorGroupClass = 'error'; - $.fn.editableform.errorBlockClass = null; + $.fn.editableform.errorBlockClass = null; + //engine + $.fn.editableform.engine = 'bs2'; }(window.jQuery)); \ No newline at end of file diff --git a/src/editable-form/editable-form-bootstrap3.js b/src/editable-form/editable-form-bootstrap3.js new file mode 100644 index 0000000..20f9645 --- /dev/null +++ b/src/editable-form/editable-form-bootstrap3.js @@ -0,0 +1,61 @@ +/* +Editableform based on Twitter Bootstrap 3 +*/ +(function ($) { + "use strict"; + + //store parent methods + var pInitInput = $.fn.editableform.Constructor.prototype.initInput; + + $.extend($.fn.editableform.Constructor.prototype, { + initTemplate: function() { + this.$form = $($.fn.editableform.template); + this.$form.find('.control-group').addClass('form-group'); + this.$form.find('.editable-error-block').addClass('help-block'); + }, + initInput: function() { + pInitInput.apply(this); + + //for bs3 set default class `input-sm` to standard inputs + var emptyInputClass = this.input.options.inputclass === null || this.input.options.inputclass === false; + var defaultClass = 'input-sm'; + + //bs3 add `form-control` class to standard inputs + var stdtypes = 'text,select,textarea,password,email,url,tel,number,range,time'.split(','); + if(~$.inArray(this.input.type, stdtypes)) { + this.input.$input.addClass('form-control'); + if(emptyInputClass) { + this.input.options.inputclass = defaultClass; + this.input.$input.addClass(defaultClass); + } + } + + //apply bs3 size class also to buttons (to fit size of control) + var $btn = this.$form.find('.editable-buttons'); + var classes = emptyInputClass ? [defaultClass] : this.input.options.inputclass.split(' '); + for(var i=0; i<classes.length; i++) { + if(classes[i].toLowerCase() === 'input-sm') { + $btn.find('button').addClass('btn-sm'); + } + if(classes[i].toLowerCase() === 'input-lg') { + $btn.find('button').addClass('btn-lg'); + } + } + } + }); + + //buttons + $.fn.editableform.buttons = + '<button type="submit" class="btn btn-primary editable-submit">'+ + '<i class="glyphicon glyphicon-ok"></i>'+ + '</button>'+ + '<button type="button" class="btn btn-default editable-cancel">'+ + '<i class="glyphicon glyphicon-remove"></i>'+ + '</button>'; + + //error classes + $.fn.editableform.errorGroupClass = 'has-error'; + $.fn.editableform.errorBlockClass = null; + //engine + $.fn.editableform.engine = 'bs3'; +}(window.jQuery)); \ No newline at end of file diff --git a/src/editable-form/editable-form-jqueryui.js b/src/editable-form/editable-form-jqueryui.js index 3c13f1f..0c7d64c 100644 --- a/src/editable-form/editable-form-jqueryui.js +++ b/src/editable-form/editable-form-jqueryui.js @@ -26,5 +26,7 @@ Editableform based on jQuery UI //error classes $.fn.editableform.errorGroupClass = null; $.fn.editableform.errorBlockClass = 'ui-state-error'; + //engine + $.fn.editableform.engine = 'jqeury-ui'; }(window.jQuery)); \ No newline at end of file diff --git a/src/editable-form/editable-form.js b/src/editable-form/editable-form.js index 5b8cbf5..b9361dc 100644 --- a/src/editable-form/editable-form.js +++ b/src/editable-form/editable-form.js @@ -28,6 +28,9 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc. //set initial value //todo: may be add check: typeof str === 'string' ? this.value = this.input.str2value(this.options.value); + + //prerender: get input.$input + this.input.prerender(); }, initTemplate: function() { this.$form = $($.fn.editableform.template); @@ -75,9 +78,8 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc. this.initInput(); //append input to form - this.input.prerender(); this.$form.find('div.editable-input').append(this.input.$tpl); - + //append form to container this.$div.append(this.$form); @@ -615,4 +617,7 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc. //error class attached to editable-error-block $.fn.editableform.errorBlockClass = 'editable-error'; + + //engine + $.fn.editableform.engine = 'jqeury'; }(window.jQuery)); diff --git a/src/element/editable-element.js b/src/element/editable-element.js index 92cdd9b..0f2e6ca 100644 --- a/src/element/editable-element.js +++ b/src/element/editable-element.js @@ -806,7 +806,7 @@ Makes editable any HTML element on the page. Applied as jQuery method. @since 1.4.5 @default #FFFF80 **/ - highlight: '#FFFF80' + highlight: '#FFFF80' }; }(window.jQuery)); diff --git a/src/inputs/abstract.js b/src/inputs/abstract.js index ba040c3..8a07490 100644 --- a/src/inputs/abstract.js +++ b/src/inputs/abstract.js @@ -159,7 +159,7 @@ To create your own input you can inherit from this class. }, // -------- helper functions -------- - setClass: function() { + setClass: function() { if(this.options.inputclass) { this.$input.addClass(this.options.inputclass); } @@ -191,9 +191,9 @@ To create your own input you can inherit from this class. @property inputclass @type string - @default input-medium + @default null **/ - inputclass: 'input-medium', + inputclass: null, //scope for external methods (e.g. source defined as function) //for internal use only scope: null, diff --git a/src/inputs/combodate/combodate.js b/src/inputs/combodate/combodate.js index dbc35d7..c3e6a7b 100644 --- a/src/inputs/combodate/combodate.js +++ b/src/inputs/combodate/combodate.js @@ -64,7 +64,14 @@ $(function(){ $.extend(Constructor.prototype, { render: function () { this.$input.combodate(this.options.combodate); + + if($.fn.editableform.engine === 'bs3') { + this.$input.siblings().find('select').addClass('form-control'); + } + if(this.options.inputclass) { + this.$input.siblings().find('select').addClass(this.options.inputclass); + } //"clear" link /* if(this.options.clear) { diff --git a/src/inputs/select2/select2.js b/src/inputs/select2/select2.js index e08edcf..ab1fe1c 100644 --- a/src/inputs/select2/select2.js +++ b/src/inputs/select2/select2.js @@ -316,7 +316,7 @@ $(function(){ @type string @default ', ' **/ - viewseparator: ', ' + viewseparator: ', ' }); $.fn.editabletypes.select2 = Constructor; diff --git a/test/loader.js b/test/loader.js index 21825fd..5f0eb73 100644 --- a/test/loader.js +++ b/test/loader.js @@ -14,15 +14,18 @@ define(function () { return { loadCss: loadCss, getConfig: function (baseUrl) { - + var jqueryui_ver = '1.10.3', // jqueryui_ver = '1.9.1', + bs_ver = '300', + bs_major_ver = bs_ver.substr(0,1), paths = { // "bootstrap": "../test/libs/bootstrap221", // "bootstrap": "../test/libs/bootstrap222", // "bootstrap": "../test/libs/bootstrap231", - "bootstrap": "../test/libs/bootstrap232", + // "bootstrap": "../test/libs/bootstrap232", + "bootstrap": "../test/libs/bootstrap"+bs_ver, // "jqueryui": "../test/libs/jquery-ui-"+jqueryui_ver+".custom", "jqueryui_js": "../test/libs/jquery-ui-"+jqueryui_ver+".custom/js/jquery-ui-"+jqueryui_ver+".custom", @@ -92,17 +95,27 @@ define(function () { init: function(require) { loadCss(require.toUrl("../css/bootstrap.css")); //add responsive css - loadCss(require.toUrl("../css/bootstrap-responsive.css")); + if(bs_major_ver < 3) { + loadCss(require.toUrl("../css/bootstrap-responsive.css")); + } } }, 'editable-form/editable-form-bootstrap': [ 'editable-form/editable-form', 'bootstrap/js/bootstrap' ], + 'editable-form/editable-form-bootstrap3': [ + 'editable-form/editable-form', + 'bootstrap/js/bootstrap' + ], 'containers/editable-popover': [ 'containers/editable-inline', 'bootstrap/js/bootstrap' ], + 'containers/editable-popover3': [ + 'containers/editable-inline', + 'bootstrap/js/bootstrap' + ], 'inputs/date/date': { deps: ['require', 'bootstrap/js/bootstrap', @@ -217,12 +230,16 @@ define(function () { if(f === 'bootstrap') { //bootstrap - shim['editable-form/editable-form'].deps.push('inputs/date/datefield'); - shim['editable-form/editable-form'].deps.push('inputs/datetime/datetimefield'); - shim['editable-form/editable-form'].deps.push('inputs-ext/wysihtml5/wysihtml5'); - shim['editable-form/editable-form'].deps.push('inputs/typeahead'); - shim['element/editable-element'].deps.push('editable-form/editable-form-bootstrap'); - shim['element/editable-element'].deps.push('containers/editable-popover'); + shim['editable-form/editable-form'].deps = shim['editable-form/editable-form'].deps.concat( + [ + 'inputs/date/datefield', + 'inputs/datetime/datetimefield', + 'inputs-ext/wysihtml5/wysihtml5', + 'inputs/typeahead' + ]); + var suffix = bs_major_ver < 3 ? '' : bs_major_ver; + shim['element/editable-element'].deps.push('editable-form/editable-form-bootstrap'+suffix); + shim['element/editable-element'].deps.push('containers/editable-popover'+suffix); } else if(f === 'jqueryui') { //jqueryui shim['editable-form/editable-form'].deps.push('inputs/dateui/dateuifield');