Check which Kinetic.Layer is on top - z-index

I have one stage and multiple layers which are superposed. I refer to each layer with a button. I must allow the user to draw on those multiple layers whenever he clicks on one button, and swap between them. How can I check which layer is on top of the others?
I tried layer.getAbsoluteZIndex() but it's not obvious. Is there a method that actually return true/false for example, maybe like .isOnTop()?
EDIT :
Well, I had to implement it by my own and it was okay I guess. The easiest solution was to add a boolean attribute isOnTop in the definition of each layer and make the appropriate tests and treatments.

First add a new method on the Kinetic.Stage:
Kinetic.Stage.prototype.getTopLayer=function(){
var layers=stage.find('Layer');
var topLayerIndex=-100;
var topLayer;
for(var i=0;i<layers.length;i++){
var index=layers[i].getAbsoluteZIndex();
if(index>topLayerIndex){
topLayerIndex=index;
topLayer=layers[i];
}
}
return(topLayer);
}
Then you can get the layer with the highest z-index like this:
var myTopLayer = stage.getTopLayer();

Related

How to prevent loops in jointjs / rappid

I'm building an application which uses jointjs / rappid and I want to be able to avoid loops from occuring across multiple cells.
Jointjs already has some examples on how to avoid this in a single cell (connecting an "out" port to an "in" port of the same cell) but has nothing on how to detect and prevent loops from occuring further up in the chain.
To help understand, imagine each cell in the paper is a step to be completed. Each step should only ever be run once. If the last step has an "out" port that connects to the "in" port of the first cell, it will just loop forever. This is what I want to avoid.
Any help is greatly appreciated.
I actually found a really easy way to do this for anyone else who wishes to achieve the same thing. Simply include the graphlib dependancy and use the following:
paper.on("link:connect", function(linkView) {
if(graphlib.alg.findCycles(graph.toGraphLib()).length > 0) {
linkView.model.remove();
// show some error message here
}
});
This line:
graphlib.alg.findCycles(graph.toGraphLib())
Returns an array that contains any loops, so by checking the length we can determine whether or not the paper contains any loops and if so, remove the link that the user is trying to create.
Note: This isn't completely full-proof because if the paper already contains a loop (before the user adds a link) then simply removing the link that the user is creating won't remove any loop that exists. For me this is fine because all of my papers will be created from scratch so as long as this logic is always in place, no loops can ever be created.
Solution through graphlib
Based on Adam's graphlib solution, instead of findCycles to test for loops, the graphlib docs suggests to use the isAcyclic function, which:
returns true if the graph has no cycles and returns false if it does. This algorithm returns as soon as it detects the first cycle.
Therefore this condition:
if(graphlib.alg.findCycles(graph.toGraphLib()).length > 0)
Can be shortened to:
if(!graphlib.alg.isAcyclic(graph))
JointJS functions solution
Look up the arrays of ancestors and successors of a newly connected element and intersect them:
// invoke inside an event which tests if a specific `connectedElement` is part of a loop
function isElementPartOfLoop (graph, connectedElement) {
var elemSuccessors = graph.getSuccessors(connectedElement, {deep: true});
var elemAncestors = connectedElement.getAncestors();
// *** OR *** graph.getPredecessors(connectedElement, {deep: true});
var commonElements = _.intersection(elemSuccessors, elemAncestors);
// if an element is repeated (non-empty intersection), then it's part of a loop
return !_.isEmpty(commonElements);
}
I haven't tested this, but the theory behind the test you are trying to accomplish should be similar.
This solution is not as efficient as using directly the graphlib functions.
Prevention
One way you could prevent the link from being added to the graph is by dealing with it in an event:
graph.on('add', _.bind(addCellOps, graph));
function addCellOps (cell, collection, opt) {
if (cell.isLink()){
// test link's target element: if it is part of a loop, remove the link
var linkTarget = cell.getTargetElement();
// `this` is the graph
if(target && isElementPartOfLoop(this, linkTarget)){
cell.remove();
}
}
// other operations ....
}

Fabric js select object in group

Is it possible to select a single object from a group created like this?
var r = new fabric.Rect(...);
var l = new fabric.Line(...);
var roadGroup = new fabric.Group([r,l],{ ... });
So I want to have a group, but select objects l or r separately.
The simple answer is yes, but you should make sure you take into account the purpose of a group.
To get a handle on an object that is wrapped in a group you can do something like this:
var r = roadGroup._objects[0];
var l = roadGroup._objects[1];
To select a child of a group try something like this:
fabricCanvas.setActiveObject(roadGroup._objects[0]);
soapbox:
The purpose of creating a group is to treat several objects as if they were a single one. The purpose of selecting an object is to allow user interactions with an object. If you want your user to interact with a portion of a group, you might want to consider not grouping them in the first place, or else un-grouping them prior to selecting the child object.
/soapbox
I believe _objects is to be used internally only and may thus change in the future.
To me it group.item(indexOfItem) seems to be the way
So I had this scenario where I have multiple images in a box. Those all images move along with the box (as a group) but user should also be able to select an individual image and move it.
Basically I wanted to select individual objects (in my case images) of group, I did it like this:
groupImages.forEach(image => image.on('mousedown', function (e) {
var group = e.target;
if (group && group._objects) {
var thisImage = group._objects.indexOf(image);
var item = group._objects[thisImage];//.find(image);
canvas.setActiveObject(item);
}
}));
groupImages could be list of objects which you want to select individually.

How to call wxGrid's Render function - sequentially to achieve pagination?

Pagination can be accomplished by using sequential Render() calls from http://docs.wxwidgets.org/trunk/classwx_grid.html
I understand How - sequential pages can be got from top left and bottom right coordinates.
But I dont get what needs to be the wxDC in the Render() call . ?
I want to get the first three rows in the grid
BigGridFrame::BigGridFrame(long sizeGrid)
: wxFrame(NULL, wxID_ANY, wxT("Plugin Virtual Table"),
wxDefaultPosition, wxSize(500, 450))
{
m_grid = new wxGrid(this, wxID_ANY, wxDefaultPosition, wxDefaultSize);
m_table = new BigGridTable(sizeGrid);
m_grid->SetTable(m_table, true);
//The above code gave me the table
//trying to get first three rows by render() function but it still gets the all of the grid values
**const wxGridCellCoords topLeft(0, 0);
const wxGridCellCoords bottomRight(3,4 );
wxClientDC clientDC(this); //this place I am not sure.. I couldnt find documentation
//m_grid->SelectBlock(topLeft, bottomRight, false);
m_grid->Render(clientDC, wxDefaultPosition, wxDefaultSize, topLeft, bottomRight, wxEXPAND);**
}
The DC is whatever you want to render the grid on, typically a wxMemoryDC to save the grid as a bitmap. This can't be used to partially render the grid on screen, which you appear to want to do, because it's just a static snapshot of the control.
I also have really no idea how can the code using this->Render() above compile considering that this is a wxFrame* and not a wxGrid*.
I misunderstood Render() function .
For pagination, I used the same logic used in
Changing Size of Grid when grid data is changed

Customize the search portlet in Plone for specific content types

I'm using the search portlet in certain areas of my website, but I'd like to restrict the results to only search for a specific content type: for example only search the news items, or only show Faculty Staff Directory profiles.
I know you can do this after you get to the ##search form through that "filter" list, but is there a way to start with the filter on, so that the "Live Search" results only show the relevant results (i.e. only news items or only profiles).
I suspect you know it already, but just to be sure: You can globally define which types should be allowed to show up in searchresults in the navigations-settings of the controlpanel, and then export and include the relevant parts to your product's GS-profile-propertiestool.xml.
However, if you would like to have some types excluded only in certain sections, you can customize Products.CMFPlone/skins/plone_scripts/livesearch_reply, which already filters the types, to only show "friendly_types" around line 38 (version 4.3.1) and add a condition like this:
Edit:
I removed the solution to check for the absolute_url of the context, because the context is actually the livesearch_reply in this case, not the current section-location. Instead the statement checks now, if the referer is our section:
REQUEST = context.REQUEST
current_location = REQUEST['HTTP_REFERER']
location_to_filter = '/fullpath/relative/to/siteroot/sectionId'
url_to_filter = str(portal_url) + location_to_filter
types_to_filter = ['Event', 'News Item']
if current_location.find(url_to_filter) != -1 or current_location.endswith(url_to_filter):
friendly_types = types_to_filter
else:
friendly_types = ploneUtils.getUserFriendlyTypes()
Yet, this leaves the case open, if the user hits the Return- or Enter-key or the 'Advanced search...'-link, landing on a different result-page than the liveresults have.
Update:
An opportunity to apply the filtering to the ##search-template can be to register a Javascript with the following content:
(function($) {
$(document).ready(function() {
// Let's see, if we are coming from our special section:
if (document.referrer.indexOf('/fullpath/relative/to/siteroot/sectionId') != -1) {
// Yes, we have the button to toggle portal_type-filter:
if ($('#pt_toggle').length>0) {
// If it's checked we uncheck it:
if ($('#pt_toggle').is(':checked')) {
$('#pt_toggle').click();
}
// If for any reason it's not checked, we check and uncheck it,
// which results in NO types to filter, for now:
else {
$('#pt_toggle').click();
$('#pt_toggle').click();
}
// Then we check types we want to filter:
$("input[value='Event']").click();
$("input[value='News Item']").click();
}
}
})
})(jQuery);
Also, the different user-actions result in different, inconsistent behaviours:
Livesearch accepts terms which are not sharp, whereas the ##search-view only accepts sharp terms or requires the user to know, that you can append an asterix for unsharp results.
When hitting the Enter/Return-key in the livesearch-input, the searchterm will be transmitted to the landing-page's (##search) input-element, whilst when clicking on 'Advanced search...' the searchterm gets lost.
Update:
To overcome the sharp results, you can add this to the JS right after the if-statement:
// Get search-term and add an asterix for blurry results:
var searchterm = decodeURI(window.location.search.replace(new RegExp("^(?:.*[&\\?]" + encodeURI('SearchableText').replace(/[\.\+\*]/g, "\\$&") + "(?:\\=([^&]*))?)?.*$", "i"), "$1")) + '*';
// Insert new searchterm in input-text-field:
$('input[name=SearchableText]').val(searchterm);
Update2:
In this related quest, Eric Brehault provides a better solution for passing the asterix during submit: Customize Plone search
Of course you can also customize the target of advanced-search-link in livesearch_reply, respectively in the JS for ##search, yet this link is rather superfluous UI-wise, imho.
Also, if you're still with Archetypes and have more use-cases for pre-filtered searchresults depending on the context, I can recommend to have a look at collective.formcriteria, which allows to define search-criteria via the UI. I love it for it's generic and straightforward plone-ish approach: catalogued indizi and collections. In contradiction to eea.facetednavigation it doesn't break accessibility and can be enhanced progressively with some live-search-js-magic with a little bit of effort, too. Kudos to Ross Patterson here! Simply turn a collection (old-style) into a searchform by changing it's view and it can be displayed as a collection-portlet, as well. And you can decide which criteria the user should be able to change or not (f.e. you hide the type-filter and offer a textsearch-input).
Watch how the query string changes when you use the filter mechanism on the ##search page. You're simply adding/subtracting catalog query criteria.
You may any of those queries in hidden fields in a search form. For example:
<form ...>
....
<input type="hidden" name="portal_type" value="Document" />
</form>
The form on the query string when you use filter is complicated a bit by its record mechanism, which allows for some min/max queries. Simple filters are much easier.

CKEditor dialogs: referencing input fields by ID

Each input field in the CKEditor dialogs are renamed with a unique number, but the number changes depending on what options are visible.
I need to reference 'txtUrl' which has an id something like #35_textInput.
So far I have discovered that something like this should work:
alert(CKEDITOR.instances.myElement.document.$.body.getId('txtUrl'));
But it doesn't. Please help.
#Rio, your solution was really close! This was the final solution:
var dialog = CKEDITOR.dialog.getCurrent();
dialog.setValueof('info','txtUrl',"http://google.com");
return false;
var dialog = this.getDialog();
var elem = dialog.getContentElement('info','txtUrl');
within an onchange part of an element I now use
dialog = this.getDialog();
alert(dialog.getContentElement('info', 'grootte').getInputElement().$.id);
and it gives 'cke_117_select' as a result. (It's a selectbox)
alert(dialog.getContentElement('info', 'txtUrl').getInputElement().$.id);
gives 'cke_107_textInput'.
I think this is what you (or other visitors to this page) are looking for.
SetValueOf still doesn't provide the id, which you may need if you want to do more than fill a text field with a certain text.

Resources