All the cells/elements are embedded on top of other cell but the links are hidden behind the element. How do I get the links on top of the element(parent)
Here's the Preview
I tried with link.toFront() which isn't working. Below is my code snippet:
paper.on('cell:pointerdown', function (cellView, evt, x, y) {
var cell = cellView.model;
if (!cell.get('embeds') || cell.get('embeds').length === 0) {
// Show the dragged element above all the other cells (except when the
// element is a parent).
cell.toFront();
link.toFront();
}
if (cell.get('parent')) {
graph.getCell(cell.get('parent')).unembed(cell);
}
});
If you want to bring a cell to the front with all connected links, try the following.
cell.toFront();
_.invoke(graph.getConnectedLinks(cell), 'toFront');
If you want to bring a parent cell with all its embedded cells to the front, call the toFront method with deep: true option. Method makes sure that all the descendants of the cell (embedded links and elements) are also brought to the front and no cell is hidden behind its parent (child z index is always higher than z index of child parent).
parent.toFront({ deep: true });
You can also checkout the embeddingMode and validateEmbedding paper options, that do the (un)embedding / validation automatically for you.
Documentation:
http://jointjs.com/api#joint.dia.Element:toFront
http://jointjs.com/api#joint.dia.Paper
Related
I want to create a textarea where users can select a part of a text, and I will react according to their selection. So I need to
1) get the start and end positions of the selection text
2) get the position of the focus, if it is in the textarea and there is no selection
It seems that the functions to do so are different from an explorer to another. So could anyone tell me what is the approach to do that in Office Add-in?
I have tried the following 2 ways (ie, select a part of the text in myTextarea, click on button, and then debug the code), they don't seem to be the right functions.
(function() {
"use strict";
Office.initialize = function(reason) {
$(document).ready(function() {
app.initialize();
$('#button').click(showSelection);
});
};
function showSelection() {
// way 1
console.log(document.selection); // undefined
document.getElementById("myTextarea").focus();
var sel = document.selection.createRange(); // Uncaught TypeError: Cannot read property 'createRange' of undefined
selectedText = sel.text;
// way 2
console.log(document.getElementById("myTextarea").selectionstart); // undefined
console.log(document.getElementById("myTextarea").selectionend); // undefined
}
})();
Additionally, it would be great if one could also tell me how to realise the follows by code:
1) select a part of a text, from a start and end positions
2) set the focus at a certain position of the textarea
Edit 1:
I just tried window.getSelection() within my Excel add-in:
function showselection() {
var a = window.getSelection();
var b = window.getSelection().toString();
var c = window.getSelection().getRangeAt(0);
}
After selecting a text in the textarea, and clicking on button, I debugged step by step: the first line made a a = Selection {anchorNode: null, anchorOffset: 0, focusNode: null, focusOffset: 0, is ...; the second line returned "", the third line got an error Home.js:19 Uncaught IndexSizeError: Failed to execute 'getRangeAt' on 'Selection': 0 is not a valid index. It looks like the selection has not been successfully caught...
Here is JSBin without Excel add-in frame, which returns almost same results as above.
Use JQuery.
For instance, the following two lines get the caret position:
function showselection() {
console.log($('#myTextarea')[0].selectionStart);
console.log($('#myTextarea')[0].selectionEnd);
}
There are some plug-ins:
https://github.com/localhost/jquery-fieldselection
http://madapaja.github.io/jquery.selection/
The second one has several short samples with buttons (where we may lose selection). You could either use their API, or look into their code to see which JQuery functions they call.
If the desired selection is just the selected text in the HTML page (and not the user's selection in Excel/Word), then there are some good stackoverflow answers about accessing that selection.
One of the key features of the JavaScript APIs for Office is that they follow an asynchronous model (the code you've written above for showSelection() appears to be synchronous). I'd recommend reading the Excel and Word JS API overview pages to get a feel for how they work. As an example, here's how you'd get the text from a selection:
Word.run(function (context) {
var myRange = context.document.getSelection();
context.load(myRange, 'text');
return context.sync().then(function () {
log("Selection contents: " + myRange.text);
});
})
Then for the other specifics of your question please clarify as requested in my comment. Thanks!
-Michael (PM for Office add-ins)
If the paper is too big for the div it's shown in, I'd like to make the paper draggable.
I tried the papers blank:pointerdown and pointerup events but was not able to just follow the mousemovement. I also tried to make the element of the paper draggable via jquery, but nothing seems to do the trick...
Is there any way to do this?
This can be achieved with a combination of JointJS events and document events. The graph display is encapsulated in a div:
<div id='diagram'></div>
Then add the event handlers for JointJS, first the pointerdown event where we store the start position of the drag:
paper.on('blank:pointerdown',
function(event, x, y) {
dragStartPosition = { x: x, y: y};
}
);
Then the end of the drag (pointerup) when we delete the variable we store the position in (it also acts as a flag whether there is an active drag in progress):
paper.on('cell:pointerup blank:pointerup', function(cellView, x, y) {
delete dragStartPosition;
});
As JointJS does not expose a "paper pointer move" event, we need to use the mousemove document event. For example, with JQuery:
$("#diagram")
.mousemove(function(event) {
if (dragStartPosition)
paper.translate(
event.offsetX - dragStartPosition.x,
event.offsetY - dragStartPosition.y);
});
We get the drag start coordinates and the current pointer position and update the paper position using the paper.translate() call.
WARNING: if you scale the paper (using paper.scale()), you have to also scale the starting position of the drag:
var scale = V(paper.viewport).scale();
dragStartPosition = { x: x * scale.sx, y: y * scale.sy};
The calls to paper.translate() will then update to the proper position.
I know this is a slightly old thread, but I was stuck with this for a while and came across a neat solution using the SVG Pan and Zoom library. Git hub link here
EDIT - I created a plunker of the steps below (plus some extras) here:
SVG panning with Jointjs
First step after creating the paper is to initialise SVG Pan and Zoom:
panAndZoom = svgPanZoom(targetElement.childNodes[0],
{
viewportSelector: targetElement.childNodes[0].childNodes[0],
fit: false,
zoomScaleSensitivity: 0.4,
panEnabled: false
});
Where targetElement is the div that the jointjs paper has gone into. Jointjs will create a SVG element within that (hence the childNodes[0]) and within that element the first element is the viewport tag (hence childNodes[0].childNodes[0] in the viewportselector). At this stage pan is disabled, because in my use case it would intefer with drag and drop elements on the paper. Instead what I do is keep a reference to the panAndZoom object and then switch pan on and off on the blank:pointerdown and blank:pointerup events:
paper.on('blank:pointerdown', function (evt, x, y) {
panAndZoom.enablePan();
});
paper.on('cell:pointerup blank:pointerup', function(cellView, event) {
panAndZoom.disablePan();
});
Just another way of tackling the issue I guess, but I found it a bit easier, plus it gives you zoom too and you can adjust the sensitivity etc.
I suggest the following:
register a handler for the paper blank:pointerdown event that will initiate the paper dragging (store a flag which you'll use in your mousemove handler to recognize the paper is in the "panning" state).
Put the big paper in a <div> container with CSS overflow: auto. This <div> will be your little window to the large paper.
register a handler for document body mousemove event (because you most likely want the paper to be dragged even if the mouse cursor leaves the paper area?). In this handler, you'll be setting the scrollLeft and scrollTop properties of your <div> container making the paper "panning". For adjusting the scrollLeft and scrollTop properties, you'll use the clientX and clientY properties of the event object together with the same properties that you stored previously in your blank:pointerdown handler. (in other words, you need those to find the offset of the panning from the last mousemove/blank:pointerdown).
register a handler for document body mouseup and in this handler, clear your paper dragging flag that you set in step 1.
I love KineticJS, its speed, marriage with GSAP, but what is making my head spin is there a way to freely transform KineticJS objects like the way in FabricJS? Here is the link reference to what I am trying to say: http://fabricjs.com/customization/
I don't want to use FabricJs as its really slow, and its low performance evident from various unit tests.
I am really looking forward to finding a way to be able to freely transform object in KineticJS as it would make life so much easier.
Is there a way to do it?
Thanks for your help,
Praney
Like markE said, this tutorial on Eric's (creator of KineticJS) tutorial site is the basis for all free transforming within KineticJS.
I'll go into detail about the actual free transform logic, there are 2 main functions:
function addAnchor(group, x, y, name) {
var stage = group.getStage();
var layer = group.getLayer();
//Create the anchor shape
var anchor = new Kinetic.Circle({
x: x,
y: y,
stroke: '#666',
fill: '#ddd',
strokeWidth: 2,
radius: 8,
name: name,
draggable: true,
dragOnTop: false
});
//Calls the update function which handles the transform logic
anchor.on('dragmove', function() {
update(this);
layer.draw();
});
//When the anchor is selected, we want to turn dragging off for the group
//This is so that only the anchor is draggable, and we can transform instead of drag
anchor.on('mousedown touchstart', function() {
group.setDraggable(false);
this.moveToTop();
});
//Turn back on draggable for the group
anchor.on('dragend', function() {
group.setDraggable(true);
layer.draw();
});
// add hover styling
anchor.on('mouseover', function() {
var layer = this.getLayer();
document.body.style.cursor = 'pointer';
this.setStrokeWidth(4);
layer.draw();
});
anchor.on('mouseout', function() {
var layer = this.getLayer();
document.body.style.cursor = 'default';
this.setStrokeWidth(2);
layer.draw();
});
group.add(anchor);
}
The addAnchor function, like the name says, adds a single anchor (or free transform handle) to a Kinetic.Group. By default it uses a Kinetic.Circle but really you can use any shape you want.
group - the group to add the anchor to
x - the x position of the anchor
y - the y position of the anchor
name - name of the anchor (usually describe which position the anchor represents, like topLeft or bottomRight
You'll notice a bunch of events attached to the newly created anchor, most of these are pretty straight-forward but the one you want to pay attention to is the dragmove event - this event is the one that calls the update function which handles all the logic for transforming the group/node. It's useful to note that the update function is called for every pixel that you are dragging an anchor.
The tutorial uses 4 corner anchors (thus calling addAnchor 4 times for each group/node), but if you wanted 8 anchors (4 corners - 4 sides) then you just have to adjust the logic to position the anchors correctly and to move the anchors properly when transforming.
By the way, the reason we're adding Anchors to a Group, is because we need them to group with the node in question, and stick with each node through dragging and transforming.
The second method is the update function:
function update(activeAnchor) {
var group = activeAnchor.getParent();
//Get each anchor inside the group, by name. Keep a standard set of names for every anchor you use and note they have to be names not ids because there will be multiple anchors named .topLeft in your app
var topLeft = group.get('.topLeft')[0];
var topRight = group.get('.topRight')[0];
var bottomRight = group.get('.bottomRight')[0];
var bottomLeft = group.get('.bottomLeft')[0];
var image = group.get('.image')[0];
var anchorX = activeAnchor.getX();
var anchorY = activeAnchor.getY();
// update anchor positions
switch (activeAnchor.getName()) {
case 'topLeft':
//When topLeft is being dragged, topRight has to update in the Y-axis
//And bottomLeft has to update in the X-axis
topRight.setY(anchorY);
bottomLeft.setX(anchorX);
break;
case 'topRight':
topLeft.setY(anchorY);
bottomRight.setX(anchorX);
break;
case 'bottomRight':
bottomLeft.setY(anchorY);
topRight.setX(anchorX);
break;
case 'bottomLeft':
bottomRight.setY(anchorY);
topLeft.setX(anchorX);
break;
}
image.setPosition(topLeft.getPosition());
//New height and width are calculated with a little math
//by calculating the distance between the update anchor positions.
var width = topRight.getX() - topLeft.getX();
var height = bottomLeft.getY() - topLeft.getY();
if(width && height) {
image.setSize(width, height);
}
}
The update function only takes one argument: the activeAnchor which is the anchor being dragged.
After that, it selects the other anchors within the group (using static names that you need to give each node and keep consistent throughout your app) so that we can translate their positions while the activeAnchor is being dragged.
The switch statement can get pretty large if you use 8 anchors instead of 4. This is because you need to consider translating almost all the other anchors while dragging one of them.
For an 8 anchor example: If you drag the topLeft anchor, you need to update the y position of the topRight anchor, the x position of the bottomLeft anchor, and for the topMid and leftMid anchors you need to adjust both the x,y values to stay in between the other anchors.
After updating the anchor position, the function handles the logic to resize the shape. Notice that the shape is selected by var image = group.get('.image')[0]; But, what you can do is use the get function to select by type and do something like:
var shape = group.get('Shape')[0];
Obviously this will work best if you just have 1 shape per group (to transform) + 4 or 8 anchors.
Let me know if you have any other questions or comments! Good luck!
this project adds a transfrom tool (resize) and some nice handlers which can be used as a basis:
https://github.com/soloproyectos/jquery.transformtool
Hi please see my previous answer about this. just search my name. hope it will be a bid help for you :) Transform (Move/Scale/Rotate) shapes with KineticJS
I forgot to include the html tags there so here it is... :)``
<!-- INSIDE THE HEAD TAGS -->
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<!-- I am using jquery transform tool for kineticjs -->
<script type="text/javascript" src="../lib/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="../lib/jquery.timer-1.0.3.js"></script>
<script type="text/javascript" src="../lib/kinetic-v4.7.4.min.js"></script>
<script type="text/javascript" src="../src/jquery.transformtool-1.0.2.js"></script>
<script type="text/javascript">
//PUT THE JAVSCRIPT SNIPPET HERE FROM THE LINK THAT I PROVIDED ABOVE
</script>
//THE ADD div element with id = canvas and file element with id = files and name files[] INSIDE THE BODY
The current state
As from my link you can pick different regions on the map and everything seems to be working until you re-select a county you have selected before. Data values stored with each path decide if it isSelected or notSelected. I have no problem in changing the element data just clicked with this but I can't find a way of storing the last element selected in a way that I can change it's element data. Which means I first have to click on the previous county to set it's element data to notSelected
First I define var currentcountyselected = "";. This allows me to store the paths[arr[this.id]].name;. When I click on a new path I can make the last path fill change with $('#'+currentcountyselected).attr({fill: attributes.fill});
In Raphael's for loop I set obj.data('selected', 'notSelected'); so all path elements are set to notSeelected.
So what I need is some way to store the last path so I can change it's element data
This is the click function cleaned up from live example.
obj.click(function(){
if(this.data('selected') == 'notSelected')
{this.animate({fill: '#698B22' }, 300);
this.data('selected', 'isSelected');
$('#'+currentcountyselected).attr({fill: attributes.fill});
paths[arr[this.id]].value = "isSelected";
currentcountyselected = paths[arr[this.id]].name;
}
else
{this.animate({fill: '#32CD32'}, 300);
paths[arr[this.id]].value = "notSelected"; /* set path value*/
this.data('selected', 'notSelected');
}
});/* end mark selections */
I've been working on this project for a while and the client now wants the interface to work differently. This has really ate up my hourse.
EDIT:Although I have found a solution by simply taking out the if/else I would still like to know how to get at element data in a previous path (or any path for that matter).
Here is my solution, posted as it might help someone. The link in my question has problems with click happy users.
Globals
var previouscountyselected = "Mayo"; /* default start, can be any county(path) */
var start = true;
var past = null;
Changed code
obj.click(function(){
if(paths[arr[this.id]].value == 'notSelected')
{
this.animate({fill: '#698B22'}, 200);
paths[previouscountyselected].value = "notSelected";
paths[arr[this.id]].value = "isSelected";
previouscountyselected = paths[arr[this.id]].name;
if (!start && past != this)
{
past.animate({ fill: '#fff' }, 200);
}
past = this;
start = false;
}
else if(paths[arr[this.id]].value == 'isSelected')
{
this.animate({fill: '#32CD32'}, 200);
paths[arr[this.id]].value = "notSelected"; /* set path value */
}
});
Overview
if (!start && past != this) is a little unusual and is required or animated fades get messed up and choppy. The fade is not triggered if it is the first time a path is clicked and if you just hammer clicks on one path it doesn't fade to white. The main if/else handles the actual control value.
Until I get a jsfiddle up this link will demonstrate the desired behaviour.
Note! the drop menu in this link does not work.
Click happy friendly
A few small questions on this web page I am putting together.
http://dansiop.com/guyc/our-team/
I am looking to get the content that appears when you click on an image to slide with the mouse as you scroll down. Not sure if this is possible, if so can anyone help.
I have tried looking but as I am such a n00b to jquery I'm not sure what I am looking for. I found some code that used the tag slide and it was not what i wanted at all so not sure what the command would be.
You need to wrap your prof_1, prof_2, etc, in a wrapper I'm going to call this wrapper prof_wrapper
<div id="prof_wrapper"></div>
jQuery(document).ready( function(){
var orig_t = jQuery("#prof_wrapper").offset().top;
var mt = jQuery("#main").offset().top;
var mh = jQuery("#main").outerHeight();
jQuery(document).bind("scroll", function(event){
var ph = jQuery("#prof_wrapper").outerHeight();
var mo = (mt+mh) - jQuery(document).scrollTop();
var t = jQuery(document).scrollTop();
if(mo <= ph){ //Find out if the #main element's offset top plus it's height minus the scroll position is less than the prof_wrapper's height; if it is, leave the prof_wrapper at the bottom of the #main element
jQuery("#prof_wrapper").css({'position':'absolute', 'bottom':'0px', 'top':'auto'});
}else if(t >= orig_t){ //Otherwise, if we have scrolled past the prof_wrapper, make the prof_wrapper follow us down
jQuery("#prof_wrapper").css({'position':'fixed', 'top':'0px', 'bottom':'auto'});
}else{ //We are probably at the top of the page, so reset the original positioning
jQuery("#prof_wrapper").css({'position':'relative', 'top':'0px', 'bottom':'auto'});
}
});
});
You need to add - position:relative to your #main element, then I think this should be pretty close. Take a look at my comment about the padding and margin on the prof_wrapper.
The positioning of absolute, assumes that #main is the first parent element to have a position assigned to it; if so, then it will use this as the positioning for the bottom.
Something like that?