How to query connections/endpoints contained in a parent div - jsplumb

Is there a way to query jsPlumb in order to retrieve all connections whose "source" and "target" properties have the same parent div? Currently, I am manually setting the "scope" property of each connection (based on the parent div's id), and this works. However, feels hacky. I feel as if there should be some way to query jsPlumb like
jsPlumb.select('#parentDiv').each(function(connection) {
/*do stuff here*/
});

At the time of connection creation itself you can check and store those connections separately instead of checking for it later. Code:
jsPlumb.bind("jsPlumbConnection", function(ci) {
var s=ci.sourceId,c=ci.targetId;
var f=$('#'+s).parent().attr("id");
var g=$('#'+c).parent().attr("id");
if( f===g ){
// store ci.connection
console.log("Connection:"+ ci.connection +" has same parent div");
}
});

This is how you can confined it to a specific parent div & use 'instance' for each jsPlumb operation :
jsPlumb.ready(function () {
instance = jsPlumb.getInstance({
DragOptions: { cursor: 'pointer', zIndex: 2000 },
ConnectionOverlays: [
["Arrow", { width: 12, length: 15, location: -3 }],
],
Container: "parent" //put parent div id here
});
});

function containsCycle() {
var return_content = false;
$('.item:visible').each(function() {
$(this).addClass("white");
});
$('.item:visible').each(function() {
$vertex = $(this);
if ($vertex.hasClass("white")) {
if (visit($vertex)) {
return_content = true;
return false;
}
}
});
return return_content;
}
function visit(vertex) {
vertex.removeClass("white")
.addClass("grey");
var vertex_connections = jsPlumb
.getConnections({
source: vertex
});
for (var i = 0; i < vertex_connections.length; i++) {
if ($('#' + vertex_connections[i].targetId)
.hasClass("grey")) {
return true;
} else if ($('#' + vertex_connections[i].targetId)
.hasClass("white")) {
if (visit($('#' + vertex_connections[i].targetId))) {
return true;
}
}
}
vertex.removeClass("grey")
.addClass("black");
return false;
}
Use this code to find the cycle connection

Related

How to make ajax call on end of each block with infinite scrolling in ag-grid?

I am using ag-grid with angular 4.
I am using infinite scrolling as the rowModelType. But since my data is huge, we want to first call just 100 records in the first ajax call and when the scroll reaches the end, the next ajax call needs to be made with the next 100 records? How can i do this using ag-grid in angular 4.
This is my current code
table-component.ts
export class AssaysTableComponent implements OnInit{
//private rowData;
private gridApi;
private gridColumnApi;
private columnDefs;
private rowModelType;
private paginationPageSize;
private components;
private rowData: any[];
private cacheBlockSize;
private infiniteInitialRowCount;
allTableData : any[];
constructor(private http:HttpClient, private appServices:AppServices) {
this.columnDefs = [
{
headerName: "Date/Time",
field: "createdDate",
headerCheckboxSelection: true,
headerCheckboxSelectionFilteredOnly: true,
checkboxSelection: true,
width: 250,
cellRenderer: "loadingRenderer"
},
{headerName: 'Assay Name', field: 'assayName', width: 200},
{headerName: 'Samples', field: 'sampleCount', width: 100}
];
this.components = {
loadingRenderer: function(params) {
if (params.value !== undefined) {
return params.value;
} else {
return '<img src="../images/loading.gif">';
}
}
};
this.rowModelType = "infinite";
//this.paginationPageSize = 10;
this.cacheBlockSize = 10;
this.infiniteInitialRowCount = 1;
//this.rowData = this.appServices.assayData;
}
ngOnInit(){
}
onGridReady(params) {
this.gridApi = params.api;
this.gridColumnApi = params.columnApi;
//const allTableData:string[] = [];
//const apiCount = 0;
//apiCount++;
console.log("assayApiCall>>",this.appServices.assayApiCall);
const assaysObj = new Assays();
assaysObj.sortBy = 'CREATED_DATE';
assaysObj.sortOder = 'desc';
assaysObj.count = "50";
if(this.appServices.assayApiCall>0){
console.log("this.allTableData >> ",this.allTableData);
assaysObj.startEvalulationKey = {
}
}
this.appServices.downloadAssayFiles(assaysObj).subscribe(
(response) => {
if (response.length > 0) {
var dataSource = {
rowCount: null,
getRows: function (params) {
console.log("asking for " + params.startRow + " to " + params.endRow);
setTimeout(function () {
console.log("response>>",response);
if(this.allTableData == undefined){
this.allTableData = response;
}
else{
this.allTableData = this.allTableData.concat(response);
}
var rowsThisPage = response.slice(params.startRow, params.endRow);
var lastRow = -1;
if (response.length <= params.endRow) {
lastRow = response.length;
}
params.successCallback(rowsThisPage, lastRow);
}, 500);
}
}
params.api.setDatasource(dataSource);
this.appServices.setIsAssaysAvailable(true);
this.appServices.assayApiCall +=1;
}
else{
this.appServices.setIsAssaysAvailable(false)
}
}
)
}
}
I will need to call this.appServices.downloadAssayFiles(assaysObj) at the end of 100 rows again to get the next set of 100 rows.
Please suggest a method of doing this.
Edit 1:
private getRowData(startRow: number, endRow: number): Observable<any[]> {
var rowData =[];
const assaysObj = new Assays();
assaysObj.sortBy = 'CREATED_DATE';
assaysObj.sortOder = 'desc';
assaysObj.count = "10";
this.appServices.downloadAssayFiles(assaysObj).subscribe(
(response) => {
if (response.length > 0) {
console.log("response>>",response);
if(this.allTableData == undefined){
this.allTableData = response;
}
else{
rowData = response;
this.allTableData = this.allTableData.concat(response);
}
this.appServices.setIsAssaysAvailable(true);
}
else{
this.appServices.setIsAssaysAvailable(false)
}
console.log("rowdata>>",rowData);
});
return Observable.of(rowData);
}
onGridReady(params: any) {
console.log("onGridReady");
var dataSource = {
getRows: (params: IGetRowsParams) => {
this.info = "Getting datasource rows, start: " + params.startRow + ", end: " + params.endRow;
console.log(this.info);
this.getRowData(params.startRow, params.endRow)
.subscribe(data => params.successCallback(data));
}
};
params.api.setDatasource(dataSource);
}
Result 1 : The table is not loaded with the data. Also for some reason the service call this.appServices.downloadAssayFiles is being made thrice . Is there something wrong with my logic here.
There's an example of doing exactly this on the ag-grid site: https://www.ag-grid.com/javascript-grid-infinite-scrolling/.
How does your code currently act? It looks like you're modeling yours from the ag-grid docs page, but that you're getting all the data at once instead of getting only the chunks that you need.
Here's a stackblitz that I think does what you need. https://stackblitz.com/edit/ag-grid-infinite-scroll-example?file=src/app/app.component.ts
In general you want to make sure you have a service method that can retrieve just the correct chunk of your data. You seem to be setting the correct range of data to the grid in your code, but the issue is that you've already spent the effort of getting all of it.
Here's the relevant code from that stackblitz. getRowData is the service call that returns an observable of the records that the grid asks for. Then in your subscribe method for that observable, you supply that data to the grid.
private getRowData(startRow: number, endRow: number): Observable<any[]> {
// This is acting as a service call that will return just the
// data range that you're asking for. In your case, you'd probably
// call your http-based service which would also return an observable
// of your data.
var rowdata = [];
for (var i = startRow; i <= endRow; i++) {
rowdata.push({ one: "hello", two: "world", three: "Item " + i });
}
return Observable.of(rowdata);
}
onGridReady(params: any) {
console.log("onGridReady");
var datasource = {
getRows: (params: IGetRowsParams) => {
this.getRowData(params.startRow, params.endRow)
.subscribe(data => params.successCallback(data));
}
};
params.api.setDatasource(datasource);
}

jqgrid top header not moving with left scroll , when used mCustomScrol(v3.1.5)

I am trying to use mCustomScrollbar on jqgrid( 4.9.2). The design of the scrollbar is getting changed but when scrolling horizontally the top headers does not move as they do normally.
The example I am trying to work on is of collapsable grid.
and for the mCustomScroll
$(".ui-jqgrid-bdiv").mCustomScrollbar({
    axis:"yx",
});
Is it not possible at all to use any custom scroll bar on jqgrid ?
I had made some customized changes to jqgr so migrating to the other version will be a tough task, so Instead, I made the changes in the mcustomscrollbar and posting the answer so if any one else come across the same problem it will be beneficial.
so there is a methods _tweetTo, which is called for the container on which the scrollbar is assigned by the initializing as
$(".ui-jqgrid-bdiv").mCustomScrollbar({
    axis:"yx",
});
now just after the method call _tweenTo(line# 2131 for V:3.1.5) insert the following code
if ($(".ui-jqgrid-hdiv").length > 0) {
$(".ui-jqgrid-view").css("overflow", "hidden");
$(".ui-jqgrid-hdiv").css("width", $("#grid1").width() + "px"); // grid1 is the id of your gridcontainer/table
_tweenTo($(".ui-jqgrid-hdiv")[0], property, Math.round(scrollTo[0]), dur[0], options.scrollEasing, options.overwrite, {
onStart: function () {
if (options.callbacks && options.onStart && !d.tweenRunning) {
/* callbacks: onScrollStart */
if (_cb("onScrollStart")) { _mcs(); o.callbacks.onScrollStart.call(el[0]); }
d.tweenRunning = true;
_onDragClasses(mCSB_dragger);
d.cbOffsets = _cbOffsets();
}
}, onUpdate: function () {
if (options.callbacks && options.onUpdate) {
/* callbacks: whileScrolling */
if (_cb("whileScrolling")) { _mcs(); o.callbacks.whileScrolling.call(el[0]); }
}
}, onComplete: function () {
if (options.callbacks && options.onComplete) {
if (o.axis === "yx") { clearTimeout(mCSB_container[0].onCompleteTimeout); }
var t = mCSB_container[0].idleTimer || 0;
mCSB_container[0].onCompleteTimeout = setTimeout(function () {
/* callbacks: onScroll, onTotalScroll, onTotalScrollBack */
if (_cb("onScroll")) { _mcs(); o.callbacks.onScroll.call(el[0]); }
if (_cb("onTotalScroll") && scrollTo[1] >= limit[1] - totalScrollOffset && d.cbOffsets[0]) { _mcs(); o.callbacks.onTotalScroll.call(el[0]); }
if (_cb("onTotalScrollBack") && scrollTo[1] <= totalScrollBackOffset && d.cbOffsets[1]) { _mcs(); o.callbacks.onTotalScrollBack.call(el[0]); }
d.tweenRunning = false;
mCSB_container[0].idleTimer = 0;
_onDragClasses(mCSB_dragger, "hide");
}, t);
}
}
});
}
and in the method definition of _tweenTo there is another method _tween
update that method as
function _tween() {
// added condition so the top headers remains fixed
if (el.classList.contains("ui-jqgrid-hdiv") && prop == "top") {
return;
}
//ends here
if (duration > 0) {
tobj.currVal = _ease(tobj.time, from, diff, duration, easing);
elStyle[prop] = Math.round(tobj.currVal) + "px";
} else {
elStyle[prop] = to + "px";
}
onUpdate.call();
}
and the scrollbar is up and running.. !!

How to force CKEditor inline widgets to preserve trailing spaces

As I reported at in this CKEditor this ticket, inline widgets in CKEditor (4.7.0) do not preserve trailing spaces, causing display issues.
Take the following simple widget:
CKEDITOR.plugins.add('spanwidget', {
requires: 'widget',
init: function (editor) {
editor.widgets.add('spanwidget', {
editables: {
content: {
selector: 'span'
}
},
upcast: function (element) {
return element.name == 'span';
}
});
}
});
When you load the following data <span>lorem </span>ipsum, you see this text in output: loremipsum (notice the missing space).
This can be seen in this JSFiddle.
How can I work around the problem (I do not control which data is loaded inside CKEditor)?
I found a workaround to force the last trailing space to be preserved. The idea is to replace the last space with a when upcasting the widget element, then remove it before downcasting it:
CKEDITOR.replace('ck', {
allowedContent: true,
extraPlugins: 'spanwidget'
});
CKEDITOR.plugins.add('spanwidget', {
requires: 'widget',
init: function (editor) {
editor.widgets.add('spanwidget', {
editables: {
content: { selector: 'span' }
},
upcast: function (element) {
// Matches?
if (element.name == 'span') {
// Ends with text?
var children = element.children,
childCount = children.length,
lastChild = childCount && children[childCount - 1];
if (lastChild instanceof CKEDITOR.htmlParser.text) {
// Replace the last space with a non breaking space
// (see https://github.com/ckeditor/ckeditor-dev/issues/605)
lastChild.value = lastChild.value.replace(/ $/, ' ');
}
// Match!
return true;
}
// No match
return false;
},
downcast: function (element) {
// Span with class "targetinfo"?
if (element.name == 'span') {
// Ends with text?
var children = element.children,
childCount = children.length,
lastChild = childCount && children[childCount - 1];
if (lastChild instanceof CKEDITOR.htmlParser.text) {
// Ends with a non breaking space?
var match = lastChild.value.match(/ $/i);
if (match) {
// Replace the non breaking space with a normal one
lastChild.value = lastChild.value.replace(/ $/i, ' ');
// Clone the element
var clone = element.clone();
// Reinsert all the children into that element
for (var i = 0; i < childCount; i++) {
clone.add(children[i]);
}
// Return the clone
return clone;
}
}
}
}
});
}
});
See updated JSFiddle here.

Using Inheritance Patterns to Organize Large jQuery Applications - how to extend the plugin?

I found this working example of Inheritance Patterns that separates business logic and framework code. I'm tempted to use it as a boilerplate, but since it is an inheritance Pattern, then how can I extend the business logic (the methods in var Speaker)?
For instance, how can I extend a walk: method into it?
/**
* Object Speaker
* An object representing a person who speaks.
*/
var Speaker = {
init: function(options, elem) {
// Mix in the passed in options with the default options
this.options = $.extend({},this.options,options);
// Save the element reference, both as a jQuery
// reference and a normal reference
this.elem = elem;
this.$elem = $(elem);
// Build the dom initial structure
this._build();
// return this so we can chain/use the bridge with less code.
return this;
},
options: {
name: "No name"
},
_build: function(){
this.$elem.html('<h1>'+this.options.name+'</h1>');
},
speak: function(msg){
// You have direct access to the associated and cached jQuery element
this.$elem.append('<p>'+msg+'</p>');
}
};
// Make sure Object.create is available in the browser (for our prototypal inheritance)
// Courtesy of Papa Crockford
// Note this is not entirely equal to native Object.create, but compatible with our use-case
if (typeof Object.create !== 'function') {
Object.create = function (o) {
function F() {} // optionally move this outside the declaration and into a closure if you need more speed.
F.prototype = o;
return new F();
};
}
$.plugin = function(name, object) {
$.fn[name] = function(options) {
// optionally, you could test if options was a string
// and use it to call a method name on the plugin instance.
return this.each(function() {
if ( ! $.data(this, name) ) {
$.data(this, name, Object.create(object).init(options, this));
}
});
};
};
// With the Speaker object, we could essentially do this:
$.plugin('speaker', Speaker);
Any ideas?
How about simply using JavaScript's regular prototype inheritance?
Consider this:
function Speaker(options, elem) {
this.elem = $(elem)[0];
this.options = $.extend(this.defaults, options);
this.build();
}
Speaker.prototype = {
defaults: {
name: "No name"
},
build: function () {
$('<h1>', {text: this.options.name}).appendTo(this.elem);
return this;
},
speak: function(message) {
$('<p>', {text: message}).appendTo(this.elem);
return this;
}
};
Now you can do:
var pp = new Speaker({name: "Porky Pig"}, $("<div>").appendTo("body"));
pp.speak("That's all folks!");
Speaker.prototype.walk = function (destination) {
$('<p>', {
text: this.options.name + " walks " + destination + ".",
css: { color: "red" }
}).appendTo(this.elem);
return this;
}
pp.walk("off the stage");
Runnable version:
function Speaker(options, elem) {
this.elem = $(elem)[0];
this.options = $.extend(this.defaults, options);
this.build();
}
Speaker.prototype = {
defaults: {
name: "No name"
},
build: function () {
$('<h1>', {text: this.options.name}).appendTo(this.elem);
return this;
},
speak: function(message) {
$('<p>', {text: message}).appendTo(this.elem);
return this;
}
};
var pp = new Speaker({name: "Porky Pig"}, $("<div>").appendTo("body"));
pp.speak("That's all folks!");
Speaker.prototype.walk = function (destination) {
$('<p>', {
text: this.options.name + " walks " + destination + ".",
css: { color: "red" }
}).appendTo(this.elem);
return this;
}
pp.walk("off the stage");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>

Rally sorting by Parent

What I Need
I would like to sort my grid/store by the Parent field, but because the Parent field that is fetched is an object, it fails to fetch any records when I put a sorter based on the Parent property. Even if I add a sorter function, it is not called. I am using a rallygrid, not sure if that makes a difference
sorters: [{
property: 'Parent',
direction: 'DESC',
sorterFn: function(one, two) {
console.log('one',one);
console.log('two',two); // console never shows these
return -1;
}
}]
What I have tried
To get around displaying the object, I have added a renderer function to the Parent column. I tried adding a doSort to the column, and that function is called, but sorting the store does not call my sorterFn, it only uses the property and direction (similar to the console.log() that fails to run above)
Here is an example of a custom App that works properly. The key is setting the default storeConfig of remoteSort to false!
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
launch: function() {
App = this;
Rally.data.ModelFactory.getModel({
type: 'PortfolioItem/Feature',
success: function(model) {
App.add({
xtype: 'rallygrid',
id : 'grid',
model: model,
columnCfgs: [
'FormattedID',
'Name',
{dataIndex: 'Parent', name: 'Parent',
doSort: function(state) {
var ds = this.up('grid').getStore();
var field = this.getSortParam();
console.log('field',field);
ds.sort({
property: field,
direction: state,
sorterFn: function(v1, v2){
v1 = v1.get(field);
v2 = v2.get(field);
console.log('v1',v1);
console.log('v2',v2);
if (!v1 && !v2) {
return 0;
} else if (!v2) {
return 1;
} else if (!v1) {
return -1;
}
return v1.Name.localeCompare(v2.Name);
}
});
},
renderer: function(value, meta, record) {
var ret = record.raw.Parent;
if (ret) {
return ret.Name;
} else {
return record.data.Name;
}
}
}
],
storeConfig: {
remoteSort: false
}
});
}
});
}
});

Resources