init
This commit is contained in:
5
src/containers/editable-container.css
Normal file
5
src/containers/editable-container.css
Normal file
@@ -0,0 +1,5 @@
|
||||
.editable-container,
|
||||
.editable-container .popover-inner {
|
||||
width: auto;
|
||||
max-width: none;
|
||||
}
|
153
src/containers/editable-container.js
Normal file
153
src/containers/editable-container.js
Normal file
@@ -0,0 +1,153 @@
|
||||
/**
|
||||
* Editable Container
|
||||
* Container can be popover, inline form or whatever
|
||||
* Container must provide following:
|
||||
* 1. methods:
|
||||
* show(),
|
||||
* hide(),
|
||||
* tip() - returns jquery object of container element
|
||||
* option()
|
||||
*
|
||||
* 2. events:
|
||||
* - save
|
||||
* - cancel
|
||||
*
|
||||
* 3. settings: trigger, value, placement
|
||||
*/
|
||||
(function ($) {
|
||||
|
||||
//Constructor
|
||||
var EditableContainer = function (element, options) {
|
||||
this.init(element, options);
|
||||
};
|
||||
|
||||
//methods
|
||||
EditableContainer.prototype = {
|
||||
containerName: null, //tbd in child class
|
||||
innerCss: null, //tbd in child class
|
||||
init: function(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = $.extend({}, $.fn.editableContainer.defaults, $.fn.editableform.utils.getConfigData(this.$element), options);
|
||||
this.initContainer();
|
||||
|
||||
//bind 'destroyed' listener to destroy container when element is removed from dom
|
||||
this.$element.on('destroyed', $.proxy(function(){
|
||||
this.destroy();
|
||||
}, this));
|
||||
},
|
||||
|
||||
initContainer: function(){
|
||||
this.call(this.options);
|
||||
},
|
||||
|
||||
initForm: function() {
|
||||
this.$form = $('<div>')
|
||||
.editableform(this.options)
|
||||
.on({
|
||||
save: $.proxy(this.save, this),
|
||||
cancel: $.proxy(this.cancel, this),
|
||||
show: $.proxy(this.setPosition, this),
|
||||
rendering: $.proxy(this.setPosition, this)
|
||||
});
|
||||
return this.$form;
|
||||
},
|
||||
|
||||
tip: function() {
|
||||
return this.container().$tip;
|
||||
},
|
||||
|
||||
//return instance of container
|
||||
container: function() {
|
||||
return this.$element.data(this.containerName);
|
||||
},
|
||||
|
||||
//call container's method
|
||||
call: function() {
|
||||
this.$element[this.containerName].apply(this.$element, arguments);
|
||||
},
|
||||
|
||||
show: function () {
|
||||
this.call('show');
|
||||
this.tip().addClass('editable-container');
|
||||
|
||||
this.initForm();
|
||||
this.tip().find(this.innerCss).empty().append(this.$form);
|
||||
this.$form.editableform('render');
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
this.call('hide');
|
||||
},
|
||||
|
||||
setPosition: function() {
|
||||
//tbd in child class
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
if(this.options.autohide) {
|
||||
this.hide();
|
||||
}
|
||||
this.$element.triggerHandler('cancel');
|
||||
},
|
||||
|
||||
save: function(e, params) {
|
||||
if(this.options.autohide) {
|
||||
this.hide();
|
||||
}
|
||||
this.$element.triggerHandler('save', params);
|
||||
},
|
||||
|
||||
option: function(key, value) {
|
||||
this.options[key] = value;
|
||||
this.call('option', key, value);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.call('destroy');
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//jQuery plugin definition
|
||||
$.fn.editableContainer = function (option) {
|
||||
var args = arguments;
|
||||
return this.each(function () {
|
||||
var $this = $(this),
|
||||
dataKey = 'editableContainer',
|
||||
data = $this.data(dataKey),
|
||||
options = typeof option === 'object' && option;
|
||||
|
||||
if (!data) {
|
||||
$this.data(dataKey, (data = new EditableContainer(this, options)));
|
||||
}
|
||||
|
||||
if (typeof option === 'string') { //call method
|
||||
data[option].apply(data, Array.prototype.slice.call(args, 1));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//store constructor
|
||||
$.fn.editableContainer.Constructor = EditableContainer;
|
||||
|
||||
//defaults - must be redefined!
|
||||
$.fn.editableContainer.defaults = {
|
||||
trigger: 'manual',
|
||||
value: null,
|
||||
placement: 'top',
|
||||
autohide: true
|
||||
};
|
||||
|
||||
/*
|
||||
* workaround to have 'destroyed' event to destroy popover when element is destroyed
|
||||
* see http://stackoverflow.com/questions/2200494/jquery-trigger-event-when-an-element-is-removed-from-the-dom
|
||||
*/
|
||||
jQuery.event.special.destroyed = {
|
||||
remove: function(o) {
|
||||
if (o.handler) {
|
||||
o.handler();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}(window.jQuery));
|
60
src/containers/editable-inline.js
Normal file
60
src/containers/editable-inline.js
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Editable Inline
|
||||
* ---------------------
|
||||
*/
|
||||
(function ($) {
|
||||
|
||||
//extend methods
|
||||
$.extend($.fn.editableContainer.Constructor.prototype, {
|
||||
containerName: 'editableform',
|
||||
innerCss: null,
|
||||
|
||||
initContainer: function(){
|
||||
//no init for container
|
||||
//only convert anim to miliseconds
|
||||
if(!this.options.anim) {
|
||||
this.options.anim = 0;
|
||||
}
|
||||
},
|
||||
|
||||
tip: function() {
|
||||
return this.$form;
|
||||
},
|
||||
|
||||
show: function () {
|
||||
this.$element.hide();
|
||||
|
||||
if(this.$form) {
|
||||
this.$form.remove();
|
||||
}
|
||||
|
||||
this.initForm();
|
||||
this.tip().addClass('editable-container').addClass('editable-inline');
|
||||
this.$form.insertAfter(this.$element);
|
||||
this.$form.show(this.options.anim);
|
||||
this.$form.editableform('render');
|
||||
},
|
||||
|
||||
hide: function () {
|
||||
this.$form.hide(this.options.anim, $.proxy(function() {
|
||||
this.$element.show();
|
||||
//return focus on element
|
||||
if (this.options.enablefocus) {
|
||||
this.$element.focus();
|
||||
}
|
||||
}, this));
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.tip().remove();
|
||||
}
|
||||
});
|
||||
|
||||
//defaults
|
||||
$.fn.editableContainer.defaults = $.extend({}, $.fn.editableContainer.defaults, {
|
||||
anim: 'fast',
|
||||
enablefocus: false
|
||||
});
|
||||
|
||||
|
||||
}(window.jQuery));
|
62
src/containers/editable-popover.js
Normal file
62
src/containers/editable-popover.js
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Editable Popover
|
||||
* ---------------------
|
||||
* requires bootstrap-popover.js
|
||||
*/
|
||||
(function ($) {
|
||||
|
||||
//extend methods
|
||||
$.extend($.fn.editableContainer.Constructor.prototype, {
|
||||
containerName: 'popover',
|
||||
innerCss: '.popover-content p',
|
||||
|
||||
option: function(key, value) {
|
||||
this.options[key] = value;
|
||||
this.container().options[key] = value;
|
||||
},
|
||||
|
||||
/**
|
||||
* move popover to new position. This function mainly copied from bootstrap-popover.
|
||||
*/
|
||||
setPosition: function () {
|
||||
var popover = this.container(),
|
||||
$tip = popover.tip(),
|
||||
inside = false,
|
||||
placement, pos, actualWidth, actualHeight, tp;
|
||||
|
||||
placement = typeof popover.options.placement === 'function' ? popover.options.placement.call(popover, $tip[0], popover.$element[0]) : popover.options.placement;
|
||||
|
||||
pos = popover.getPosition(inside);
|
||||
|
||||
actualWidth = $tip[0].offsetWidth;
|
||||
actualHeight = $tip[0].offsetHeight;
|
||||
|
||||
switch (inside ? placement.split(' ')[1] : placement) {
|
||||
case 'bottom':
|
||||
tp = {top:pos.top + pos.height, left:pos.left + pos.width / 2 - actualWidth / 2};
|
||||
break;
|
||||
case 'top':
|
||||
/* For Bootstrap 2.1.0 - 2.1.1: 10 pixels needed to correct popover position. See https://github.com/twitter/bootstrap/issues/4665 */
|
||||
//if($tip.find('.arrow').get(0).offsetHeight === 10) {actualHeight += 10;}
|
||||
tp = {top:pos.top - actualHeight, left:pos.left + pos.width / 2 - actualWidth / 2};
|
||||
break;
|
||||
case 'left':
|
||||
/* For Bootstrap 2.1.0 - 2.1.1: 10 pixels needed to correct popover position. See https://github.com/twitter/bootstrap/issues/4665 */
|
||||
//if($tip.find('.arrow').get(0).offsetWidth === 10) {actualWidth += 10;}
|
||||
tp = {top:pos.top + pos.height / 2 - actualHeight / 2, left:pos.left - actualWidth};
|
||||
break;
|
||||
case 'right':
|
||||
tp = {top:pos.top + pos.height / 2 - actualHeight / 2, left:pos.left + pos.width};
|
||||
break;
|
||||
}
|
||||
|
||||
$tip.css(tp).addClass(placement).addClass('in');
|
||||
}
|
||||
});
|
||||
|
||||
//defaults
|
||||
$.fn.editableContainer.defaults = $.extend({}, $.fn.popover.defaults, $.fn.editableContainer.defaults, {
|
||||
content: ' '
|
||||
});
|
||||
|
||||
}(window.jQuery));
|
179
src/containers/editable-poshytip.js
Normal file
179
src/containers/editable-poshytip.js
Normal file
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* Editable Poshytip
|
||||
* ---------------------
|
||||
* requires jquery.poshytip.js
|
||||
*/
|
||||
(function ($) {
|
||||
|
||||
//extend methods
|
||||
$.extend($.fn.editableContainer.Constructor.prototype, {
|
||||
containerName: 'poshytip',
|
||||
innerCss: 'div.tip-inner',
|
||||
|
||||
initContainer: function(){
|
||||
this.handlePlacement();
|
||||
this.call(this.options);
|
||||
|
||||
var $content = $('<div>')
|
||||
.append($('<label>').text(this.options.title || this.$element.data( "ui-tooltip-title") || this.$element.data( "originalTitle")))
|
||||
.append(this.initForm());
|
||||
|
||||
this.call('update', $content);
|
||||
},
|
||||
|
||||
show: function () {
|
||||
this.$form.editableform('render');
|
||||
this.tip().addClass('editable-container');
|
||||
|
||||
|
||||
this.call('show');
|
||||
this.$form.data('editableform').input.activate();
|
||||
},
|
||||
|
||||
setPosition: function() {
|
||||
this.container().refresh(false);
|
||||
},
|
||||
|
||||
handlePlacement: function() {
|
||||
var x, y, ox = 0, oy = 0;
|
||||
switch(this.options.placement) {
|
||||
case 'top':
|
||||
x = 'center';
|
||||
y = 'top';
|
||||
oy = 5;
|
||||
break;
|
||||
case 'right':
|
||||
x = 'right';
|
||||
y = 'center';
|
||||
ox = 10;
|
||||
break;
|
||||
case 'bottom':
|
||||
x = 'center';
|
||||
y = 'bottom';
|
||||
oy = 5;
|
||||
break;
|
||||
case 'left':
|
||||
x = 'left';
|
||||
y = 'center';
|
||||
ox = 10;
|
||||
break;
|
||||
}
|
||||
|
||||
this.options.alignX = x;
|
||||
this.options.offsetX = ox;
|
||||
|
||||
this.options.alignY = y;
|
||||
this.options.offsetY = oy;
|
||||
}
|
||||
});
|
||||
|
||||
//defaults
|
||||
$.fn.editableContainer.defaults = $.extend({}, $.fn.poshytip.defaults, $.fn.editableContainer.defaults, {
|
||||
className: 'tip-yellowsimple',
|
||||
showOn: 'none',
|
||||
content: '',
|
||||
alignTo: 'target'
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Poshytip fix: disable incorrect table display
|
||||
* see https://github.com/vadikom/poshytip/issues/7
|
||||
*/
|
||||
/*jshint eqeqeq:false, curly: false*/
|
||||
var tips = [],
|
||||
reBgImage = /^url\(["']?([^"'\)]*)["']?\);?$/i,
|
||||
rePNG = /\.png$/i,
|
||||
ie6 = $.browser.msie && $.browser.version == 6;
|
||||
|
||||
$.Poshytip.prototype.refresh = function(async) {
|
||||
if (this.disabled)
|
||||
return;
|
||||
|
||||
var currPos;
|
||||
if (async) {
|
||||
if (!this.$tip.data('active'))
|
||||
return;
|
||||
// save current position as we will need to animate
|
||||
currPos = {left: this.$tip.css('left'), top: this.$tip.css('top')};
|
||||
}
|
||||
|
||||
// reset position to avoid text wrapping, etc.
|
||||
this.$tip.css({left: 0, top: 0}).appendTo(document.body);
|
||||
|
||||
// save default opacity
|
||||
if (this.opacity === undefined)
|
||||
this.opacity = this.$tip.css('opacity');
|
||||
|
||||
// check for images - this code is here (i.e. executed each time we show the tip and not on init) due to some browser inconsistencies
|
||||
var bgImage = this.$tip.css('background-image').match(reBgImage),
|
||||
arrow = this.$arrow.css('background-image').match(reBgImage);
|
||||
|
||||
if (bgImage) {
|
||||
var bgImagePNG = rePNG.test(bgImage[1]);
|
||||
// fallback to background-color/padding/border in IE6 if a PNG is used
|
||||
if (ie6 && bgImagePNG) {
|
||||
this.$tip.css('background-image', 'none');
|
||||
this.$inner.css({margin: 0, border: 0, padding: 0});
|
||||
bgImage = bgImagePNG = false;
|
||||
} else {
|
||||
this.$tip.prepend('<table class="fallback" border="0" cellpadding="0" cellspacing="0"><tr><td class="tip-top tip-bg-image" colspan="2"><span></span></td><td class="tip-right tip-bg-image" rowspan="2"><span></span></td></tr><tr><td class="tip-left tip-bg-image" rowspan="2"><span></span></td><td></td></tr><tr><td class="tip-bottom tip-bg-image" colspan="2"><span></span></td></tr></table>')
|
||||
.css({border: 0, padding: 0, 'background-image': 'none', 'background-color': 'transparent'})
|
||||
.find('.tip-bg-image').css('background-image', 'url("' + bgImage[1] +'")').end()
|
||||
.find('td').eq(3).append(this.$inner);
|
||||
}
|
||||
// disable fade effect in IE due to Alpha filter + translucent PNG issue
|
||||
if (bgImagePNG && !$.support.opacity)
|
||||
this.opts.fade = false;
|
||||
}
|
||||
// IE arrow fixes
|
||||
if (arrow && !$.support.opacity) {
|
||||
// disable arrow in IE6 if using a PNG
|
||||
if (ie6 && rePNG.test(arrow[1])) {
|
||||
arrow = false;
|
||||
this.$arrow.css('background-image', 'none');
|
||||
}
|
||||
// disable fade effect in IE due to Alpha filter + translucent PNG issue
|
||||
this.opts.fade = false;
|
||||
}
|
||||
|
||||
var $table = this.$tip.find('table.fallback');
|
||||
if (ie6) {
|
||||
// fix min/max-width in IE6
|
||||
this.$tip[0].style.width = '';
|
||||
$table.width('auto').find('td').eq(3).width('auto');
|
||||
var tipW = this.$tip.width(),
|
||||
minW = parseInt(this.$tip.css('min-width'), 10),
|
||||
maxW = parseInt(this.$tip.css('max-width'), 10);
|
||||
if (!isNaN(minW) && tipW < minW)
|
||||
tipW = minW;
|
||||
else if (!isNaN(maxW) && tipW > maxW)
|
||||
tipW = maxW;
|
||||
this.$tip.add($table).width(tipW).eq(0).find('td').eq(3).width('100%');
|
||||
} else if ($table[0]) {
|
||||
// fix the table width if we are using a background image
|
||||
// IE9, FF4 use float numbers for width/height so use getComputedStyle for them to avoid text wrapping
|
||||
// for details look at: http://vadikom.com/dailies/offsetwidth-offsetheight-useless-in-ie9-firefox4/
|
||||
$table.width('auto').find('td').eq(3).width('auto').end().end().width(document.defaultView && document.defaultView.getComputedStyle && parseFloat(document.defaultView.getComputedStyle(this.$tip[0], null).width) || this.$tip.width()).find('td').eq(3).width('100%');
|
||||
}
|
||||
this.tipOuterW = this.$tip.outerWidth();
|
||||
this.tipOuterH = this.$tip.outerHeight();
|
||||
|
||||
this.calcPos();
|
||||
|
||||
// position and show the arrow image
|
||||
if (arrow && this.pos.arrow) {
|
||||
this.$arrow[0].className = 'tip-arrow tip-arrow-' + this.pos.arrow;
|
||||
this.$arrow.css('visibility', 'inherit');
|
||||
}
|
||||
|
||||
if (async) {
|
||||
this.asyncAnimating = true;
|
||||
var self = this;
|
||||
this.$tip.css(currPos).animate({left: this.pos.l, top: this.pos.t}, 200, function() { self.asyncAnimating = false; });
|
||||
} else {
|
||||
this.$tip.css({left: this.pos.l, top: this.pos.t});
|
||||
}
|
||||
};
|
||||
/*jshinteqeqeq: true, curly: true*/
|
||||
}(window.jQuery));
|
97
src/containers/editable-tooltip.js
Normal file
97
src/containers/editable-tooltip.js
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Editable jQuery UI Tooltip
|
||||
* ---------------------
|
||||
* requires jquery ui 1.9.x
|
||||
*/
|
||||
(function ($) {
|
||||
|
||||
//extend methods
|
||||
$.extend($.fn.editableContainer.Constructor.prototype, {
|
||||
containerName: 'tooltip',
|
||||
innerCss: '.ui-tooltip-content',
|
||||
|
||||
initContainer: function(){
|
||||
this.handlePlacement();
|
||||
this.options.open = $.proxy(function() {
|
||||
//disable events hiding tooltip by default
|
||||
this.container()._on(this.container().element, {
|
||||
mouseleave: function(e){ e.stopImmediatePropagation(); },
|
||||
focusout: function(e){ e.stopImmediatePropagation(); }
|
||||
});
|
||||
}, this);
|
||||
this.call(this.options);
|
||||
//disable standart event to show tooltip
|
||||
this.container()._off(this.container().element, 'mouseover focusin');
|
||||
},
|
||||
|
||||
tip: function() {
|
||||
return this.container()._find(this.container().element);
|
||||
},
|
||||
|
||||
show: function() {
|
||||
this.call('open');
|
||||
this.tip().addClass('editable-container');
|
||||
|
||||
this.initForm();
|
||||
this.tip().find(this.innerCss)
|
||||
.empty()
|
||||
.append($('<label>').text(this.options.title || this.$element.data( "ui-tooltip-title") || this.$element.data( "originalTitle")))
|
||||
.append(this.$form);
|
||||
this.$form.editableform('render');
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
this.call('close');
|
||||
},
|
||||
|
||||
setPosition: function() {
|
||||
this.tip().position( $.extend({
|
||||
of: this.$element
|
||||
}, this.options.position ) );
|
||||
},
|
||||
|
||||
handlePlacement: function() {
|
||||
var pos;
|
||||
switch(this.options.placement) {
|
||||
case 'top':
|
||||
pos = {
|
||||
my: "center bottom-5",
|
||||
at: "center top"
|
||||
};
|
||||
break;
|
||||
case 'right':
|
||||
pos = {
|
||||
my: "left+5 center",
|
||||
at: "right center"
|
||||
};
|
||||
break;
|
||||
case 'bottom':
|
||||
pos = {
|
||||
my: "center top+5",
|
||||
at: "center bottom"
|
||||
};
|
||||
break;
|
||||
case 'left':
|
||||
pos = {
|
||||
my: "right-5 center",
|
||||
at: "left center"
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
this.options.position = $.extend({}, this.options.position, pos);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
//jqueryui tooltip destroy itself
|
||||
}
|
||||
});
|
||||
|
||||
//defaults
|
||||
$.fn.editableContainer.defaults = $.extend({}, $.fn.tooltip.defaults, $.fn.editableContainer.defaults, {
|
||||
items: '*',
|
||||
content: ' ',
|
||||
position: {}
|
||||
});
|
||||
|
||||
}(window.jQuery));
|
Reference in New Issue
Block a user