How to catch the deselection event in Add-on SDK in Firefox - firefox-addon

I use the Add-on Builder & SDK to develop Firefox add-on. I catch the event when users select a piece of text by the snippet:
var selection = require("sdk/selection");
selection.on('select', function () {
//Doing something
});
However, what I want also is to do other things when users does not select that text anymore, but I can not figure it out how to do it. Anyone can help me with this? Thank you very much.

I am not aware if there are events on Deselection of a text.
However, you could register for Mouse click event on body or div containing that text and then in the callback function of the event, you could check if last selected texts is currently selected or not.
var selection = require("sdk/selection");
var lastText;
function addSelection(){
var selection = false;
var body = document.body;
if(!selection.text){
//deselection
//remove DOM listeners
body.removeListener('click', addSelection);
}
if (!selection.isContiguous) {
for (var subselection in selection) {
if(subselection.text == lastText){
selection = true;
}
}
} else if(selection.text && selection.text == lastText){
selection = true;
}
if(!selection){
//deselected
//remove DOM listeners
}
}
selection.on('select', function () {
var body;
if(lastText !== selection.text){
//deselected
//remove listeners
};
lastText = selection.text;
body = document.body;
//add DOM listeners like click - that can potentially remove selection
body.addEventListener('click', addSelection);
});

Related

$(window).scroll(function () {}) is not working

I'm trying to perform some action on window scroll event but it is not working.
Here is my code
$(window).scroll(function () {
// var limit = 7; //The number of records to display per request
var lastID = $newsfeed_start;
if (($(window).scrollTop() == $(document).height() - $(window).height()&& (lastID != 0)) {
// increment strat value
$newsfeed_start = $newsfeed_start + $newsfeed_limit;
get_timeline_post('');
}
});
even $(window).scroll(function () { } ) function is not working
Your CSS is actually setting the rest of the document to not show overflow therefore the document itself isn't scrolling. The easiest fix for this is bind the event to the thing that is scrolling, which in your case is div#page.
So its easy as changing:
$(document).scroll(function() { // OR $(window).scroll(function() {
didScroll = true;
});
to
$('div#page').scroll(function() {
didScroll = true;
});

Removing a shape from Kinetic.Group

I have a Kinetic.Group that contains inside of it some shapes and images. I defined a function to delete images on double-click, here's my code :
img.on("dblclick dbltap", function (e) {
if (e.which != 3) {
img.getParent().listening(false); //the parent here is the group
var r = confirm("Are you sure you want to delete this equipment?");
if (r == true) {
this.remove();
layer.draw(this);
stage.add(layer);
img.getParent().listening(true);
}
}
});
Knowing that the group has the exact same function for deleting (except of course I'm not calling the listening() method).
group.on("dblclick dbltap", function (e) {
if (e.which != 3) {
var r = confirm("Do you want to delete this?\n\nWarning: You risk to lose its children.");
if (r == true) {
this.removeChildren();
this.remove();
layer.draw();
stage.add(layer);
}
}
});
The problem comes when I double-click on an image and click ok, so I'm getting two popups:
the first one is asking to confirm if I want to delete the image
the second asks if I want to delete the group.
Doesn't .listening(false); disable listening to events in KineticJS? What am I doing wrong?
What you're trying to achieve, is that the event doesn't propagate through the children elements. In KineticJS you can do this with event.cancelBubble = true;
So you can you use:
group.on('click', function(e) {
e.cancelBubble = true;
});
See HTML5 Canvas Cancel Event Bubble Propagation with KineticJS

Angularjs: jquery selectable

i have created a directive to handle selectable provided by Jquery
mydirectives.directive('uiSelectable', function ($parse) {
return {
link: function (scope, element, attrs, ctrl) {
element.selectable({
stop: function (evt, ui) {
var collection = scope.$eval(attrs.docArray)
var selected = element.find('div.parent.ui-selected').map(function () {
var idx = $(this).index();
return { document: collection[idx] }
}).get();
scope.selectedItems = selected;
scope.$apply()
}
});
}
}
});
to use in html
<div class="margin-top-20px" ui-selectable doc-array="documents">
where documents is an array that get returned by server in ajax response.
its working fine i can select multiple items or single item
Issue: i want to clear selection on close button
http://plnkr.co/edit/3cSef9h7MeYSM0cgYUIX?p=preview
i can write jquery in controller to remove .ui-selected class but its not recommended approach
can some one guide me whats the best practice to achieve these type of issue
Update:
i fixed the issue by broadcasting event on cancel and listening it on directive
$scope.clearSelection=function() {
$scope.selectedItems = [];
$timeout(function () {
$rootScope.$broadcast('clearselection', '');
}, 100);
}
and in directive
scope.$on('clearselection', function (event, document) {
element.find('.ui-selected').removeClass('ui-selected')
});
is this the right way of doing it or what is the best practice to solve the issue.
http://plnkr.co/edit/3cSef9h7MeYSM0cgYUIX?p=preview

Angular view gets out of sync with model when dragging items from one list to another

I have created a custom directive that allows me to connect multiple sortable lists via drag and drop using angular js and jquery ui. The way it should work is the following:
When drag starts, keep track of the initial position of the item in the array and the value of ng-model for that sortable
When the drag ends, if the item is received to a different list, keep track of the ng-model of that list and the target position of the element
Broadcast an event with that data so that the controller can change the positions of the items from one array to another
The problem is that once I move one item from one list to another, even though the items in the arrays go where they should, in the view some HTML elements disappear.
Here is the sortable directive:
app.directive('mySortable',function(){
return {
link:function(scope,el,attrs){
var options = {};
if(attrs.connectWith)
{
options.connectWith = attrs.connectWith;
}
el.sortable(options);
el.disableSelection();
el.on("sortstart", function(event, ui){
var from_index = angular.element(ui.item).scope()?angular.element(ui.item).scope().$index : 0;
var from_model = angular.element(ui.item.parent()).attr('ng-model');
ui.item.scope().sortableData = {from_index: from_index, from_model: from_model};
});
el.on("sortreceive", function(event, ui){
ui.item.scope().sortableData.to_index = el.children().index(ui.item);
ui.item.scope().sortableData.to_model = angular.element(el).attr('ng-model');
});
el.on( "sortdeactivate", function( event, ui ) {
var to_model = angular.element(el).attr('ng-model');
var from = angular.element(ui.item).scope()?angular.element(ui.item).scope().$index : 0;
var to = el.children().index(ui.item);
if(to>=0){
scope.$apply(function(){
if(from>=0){
scope.$emit('list-sorted', {from:from,to:to}, ui.item.scope());
}else{
scope.$emit('list-appended', {to:to, name:ui.item.text()});
ui.item.remove();
}
})
}
} );
}
}
})
And here is the controller logic that handles it's event:
$scope.$on('list-sorted', function(ev, val, task_scope){
var sd = task_scope.sortableData;
if(sd.to_model)
{
$timeout(function(){
$scope[sd.to_model].splice(sd.to_index, 0, $scope[sd.from_model].splice(sd.from_index, 1)[0]);
});
}
else
{
$timeout(function(){
$scope[sd.from_model].splice(val.to, 0, $scope[sd.from_model].splice(val.from, 1)[0]);
});
}
console.log($scope);
});
What's wrong?
Example JS Fiddle
It seems that the controller logic comports an error.
Is it fine like this:
var sd = item_scope.sortableData;
// If the item is supposed to be dropped to a different list, move it from one list to another
if(sd.to_model)
{
console.log("to a different list", val)
$timeout(function(){
$scope[sd.to_model].splice(val.to, 0, $scope[sd.from_model].splice(sd.from_index, 0));
});
}
else
{
console.log("to the same list")
$timeout(function(){
$scope[sd.from_model].splice(val.to, 0, $scope[sd.from_model].splice(val.from, 1)[0]);
});
}

Latency issue with Primefaces overlayPanel - loads to lazy

I am using Primefaces 3.2 with jsf 2 and glassfish 3.1.2.
I have a p:dataTable of users containing avatars of the user. Whenever the user moves the mouse over the avatar a p:overlayPanel appears with more information (lazy loaded) on the user, and disappears when the user moves the cursor away - like this:
<p:overlayPanel for="avatar" dynamic="true" showEvent="mouseover" hideEvent="mouseout" ...>
This works very well - as long as the user is "slowhanded". Whenever an user moves the cursor fast above many avatars many of the overlayPanels stay visible.
For example when the user has the cursor over the position where user avatars are displayed and uses the scroll wheel of his mouse to scroll the usertable down or up.
I believe that the overlaypanel starts to load the information dynamically (dynamic="true") from the server when showEvent="mouseover" is dispatched and displays the overlaypanel after the response from the server arrives.
This way it is not possible to detect whether the cursor is already away when the overlaypanel becomes visible - so the hideEvent="mouseout" is never dispatched.
Is there a way to make the primefaces overlaypanel appear directly on mousover, showing a loading gif and update the content into the overlaypanel when the response comes from the server.
Is this a good appraoch or does anyone know any other way to solve this nasty problem?
Thanks Pete
As my first answer is already very long and contains valid information, I decided to open a new answer presenting my final approach.
Im now using Primefaces inheritance pattern making the code alot cleaner. Also I noticed that replacing/overwriting the whole bindEvents function isnt necessary, as we can remove the old event handlers. Finally this code fixs the latest issue experienced: A hide event before ajax arrival.
PrimeFaces.widget.OverlayPanel = PrimeFaces.widget.OverlayPanel
.extend({
bindEvents : function() {
this._super();
var showEvent = this.cfg.showEvent + '.ui-overlay', hideEvent = this.cfg.hideEvent
+ '.ui-overlay';
$(document).off(showEvent + ' ' + hideEvent, this.targetId).on(
showEvent, this.targetId, this, function(e) {
var _self = e.data;
clearTimeout(_self.timer);
_self.timer = setTimeout(function() {
_self.hidden = false;
_self.show();
}, 300);
}).on(hideEvent, this.targetId, this, function(e) {
var _self = e.data;
clearTimeout(_self.timer);
_self.hidden = true;
_self.hide();
});
},
_show : function() {
if (!this.cfg.dynamic || !this.hidden) {
this._super();
}
}
});
Im sorry for the poor formatting: Eclipses fault ;)
Wow, finally after a long debuging session and testing various approaches i recognized that the problem isnt the ajax request but the event handlers itself:
.on(hideEvent, this.targetId, this, function(e) {
var _self = e.data;
if(_self.isVisible()) {
_self.hide();
}
});
As you can see, the widget is just hidden if its visible before. If your moving your mouse out too fast, now two things can happen:
The widget isnt visible at all
The animation is still going on
In this case the event is discarded and the panel stays visible. As animations are queued, one simply has to remove the if statement to fix the issue. I did this by replacing the whole bindEvents method:
PrimeFaces.widget.OverlayPanel.prototype.bindEvents = function() {
//mark target and descandants of target as a trigger for a primefaces overlay
this.target.data('primefaces-overlay-target', this.id).find('*').data('primefaces-overlay-target', this.id);
//show and hide events for target
if(this.cfg.showEvent == this.cfg.hideEvent) {
var event = this.cfg.showEvent;
$(document).off(event, this.targetId).on(event, this.targetId, this, function(e) {
e.data.toggle();
});
}
else {
var showEvent = this.cfg.showEvent + '.ui-overlay',
hideEvent = this.cfg.hideEvent + '.ui-overlay';
$(document).off(showEvent + ' ' + hideEvent, this.targetId).on(showEvent, this.targetId, this, function(e) {
var _self = e.data;
if(!_self.isVisible()) {
_self.show();
}
})
.on(hideEvent, this.targetId, this, function(e) {
var _self = e.data;
_self.hide();
});
}
//enter key support for mousedown event
this.bindKeyEvents();
var _self = this;
//hide overlay when mousedown is at outside of overlay
$(document.body).bind('mousedown.ui-overlay', function (e) {
if(_self.jq.hasClass('ui-overlay-hidden')) {
return;
}
//do nothing on target mousedown
var target = $(e.target);
if(_self.target.is(target)||_self.target.has(target).length > 0) {
return;
}
//hide overlay if mousedown is on outside
var offset = _self.jq.offset();
if(e.pageX < offset.left ||
e.pageX > offset.left + _self.jq.outerWidth() ||
e.pageY < offset.top ||
e.pageY > offset.top + _self.jq.outerHeight()) {
_self.hide();
}
});
//Hide overlay on resize
var resizeNS = 'resize.' + this.id;
$(window).unbind(resizeNS).bind(resizeNS, function() {
if(_self.jq.hasClass('ui-overlay-visible')) {
_self.hide();
}
});
};
Execute this code on load and the issue should be gone.
As your replacing the js code nevertheless, you can use this oppurtunity to implement quite a nice feature. By using timeouts in the event handlers one can easily implement a little delay not just improving usability (no more thousands of popups appear) but also reducing network traffic:
$(document).off(showEvent + ' ' + hideEvent, this.targetId).on(showEvent, this.targetId, this, function(e) {
var _self = e.data;
_self.timer = setTimeout( function(){
if(!_self.isVisible()) {
_self.show();
}
}, 300);
})
.on(hideEvent, this.targetId, this, function(e) {
var _self = e.data;
clearTimeout(_self.timer);
_self.hide();
});
Ofcourse you can use a global variable to control the delay time. If you want a more flexible approach youll have to overwrite the encodeScript method in the OverlayPanelRender to transmit an additional property. You could access it then with _self.cfg.delay. Notice though that youll have to replace the component model OverlayPanel too providing it with an extra attribute.
At the same time I thank you for this brilliant solution I take the opportunity to update it for Primefaces 5.2. In our application the code broke after that upgrade.
Follows the updated code for Primefaces 5.2:
PrimeFaces.widget.OverlayPanel.prototype.bindTargetEvents = function() {
var $this = this;
//mark target and descandants of target as a trigger for a primefaces overlay
this.target.data('primefaces-overlay-target', this.id).find('*').data('primefaces-overlay-target', this.id);
//show and hide events for target
if(this.cfg.showEvent === this.cfg.hideEvent) {
var event = this.cfg.showEvent;
this.target.on(event, function(e) {
$this.toggle();
});
}
else {
var showEvent = this.cfg.showEvent + '.ui-overlaypanel',
hideEvent = this.cfg.hideEvent + '.ui-overlaypanel';
this.target
.off(showEvent + ' ' + hideEvent)
.on(showEvent, function(e) {
clearTimeout($this.timer);
$this.timer = setTimeout(function() {
$('.ui-overlaypanel').hide();
$this.hidden = false;
$this.show();
}, 500);
})
.on(hideEvent, function(e) {
clearTimeout($this.timer);
$this.timer = setTimeout(function() {
// don't hide if hovering overlay
if(! $this.jq.is(":hover")) {
$this.hide();
}
}, 100);
});
}
$this.target.off('keydown.ui-overlaypanel keyup.ui-overlaypanel').on('keydown.ui-overlaypanel', function(e) {
var keyCode = $.ui.keyCode, key = e.which;
if(key === keyCode.ENTER||key === keyCode.NUMPAD_ENTER) {
e.preventDefault();
}
})
.on('keyup.ui-overlaypanel', function(e) {
var keyCode = $.ui.keyCode, key = e.which;
if(key === keyCode.ENTER||key === keyCode.NUMPAD_ENTER) {
$this.toggle();
e.preventDefault();
}
});
};
I also added an extra feature which allows the user to move the mouse over the overlay without hiding it. It should hide when you move the mouse out of it then which I accomplished through:
<p:overlayPanel .... onShow="onShowOverlayPanel(this)" ...>
function onShowOverlayPanel(ovr) {
ovr.jq.on("mouseleave", function(e) {
ovr.jq.hide();
});
}
Hope you enjoy!
It's been a long time, but in case anyone bumps into this problem, a showDelay attribute was added to the overlayPanel to solve this problem starting from Primefaces 6.2. However, it is not in the official documentation for some reason.

Resources