creating blockly block connections to blocks in code - blockly

I have implemented a context menu handler that adds a block to the workspace. I am trying to add the block in between the block that summoned the context menu and any blocks that may already be connected to it ( previousConnection ). What I'm shooting for and the code I've got and what it does...
context menu option handler:
var option =
{
text: "Comment",
enabled: true,
callback: function ()
{
var comment = workspace.newBlock('ta_comment');
var block = Blockly.ContextMenu.currentBlock;
block.previousConnection.connect(comment.nextConnection);
comment.initSvg();
comment.render();
}
}
menuOptions.push(option);
before
after

//create comment block and get the block that summoned the context menu
var comment = workspace.newBlock('ta_comment');
var block = Blockly.ContextMenu.currentBlock;
//connect up comment block
comment.previousConnection.connect(block.previousConnection.targetConnection);
comment.nextConnection.connect(block.previousConnection);
//init
comment.initSvg();
//render updated workspace
workspace.render();

Related

addTools is giving exception in rappidjs

In context of one of my assignment I was trying to use RappidJS. I was trying the below code for connecting an element by dropping it over another element.
https://resources.jointjs.com/tutorial/connecting-by-dropping
But I am getting exception as below
rappid.min.js:14 Uncaught TypeError: Cannot read property 'start' of null
at child.update (rappid.min.js:14)
at child.onRender (rappid.min.js:14)
at child.e.render (rappid.min.js:14)
at child.configure (rappid.min.js:14)
at child.addTools (rappid.min.js:14)
at child.element:pointerup (diagram.js:106)
at triggerEvents (backbone.js:338)
at triggerApi (backbone.js:322)
at eventsApi (backbone.js:110)
at child.Events.trigger (backbone.js:312)
I also took reference from below link
https://resources.jointjs.com/tutorial/link-tools .
Can any one suggest me what wrong I am doing here.
My code snippet is as below
function attachLinks(paper) {
paper.on({
'element:pointerdown': function (elementView, event) {
event.data = elementView.model.position();
},
'element:pointerup': function (elementView, event, x, y) {
var coordinates = new joint.g.Point(x, y);
var elementAbove = elementView.model;
var elementBelow = this.model.findModelsFromPoint(coordinates).find(function (el) {
return (el.id !== elementAbove.id);
});
// If the two elements are connected already, don't
// connect them again (this is application-specific though).
if (elementBelow && self.graph.getNeighbors(elementBelow).indexOf(elementAbove) === -1) {
// Move the element to the position before dragging.
elementAbove.position(event.data.x, event.data.y);
// Create a connection between elements.
var link = new joint.shapes.standard.Link();
link.source(elementAbove);
link.target(elementBelow);
link.addTo(this.model);
// Add remove button to the link.
var removeLinkTool = new joint.linkTools.Remove();
var tools = new joint.dia.ToolsView({
tools: [ removeLinkTool]
});
var linkView = link.findView(this);
linkView.addTools(tools); // getting exception at this place.
}
}
});
}
This issue is reproducible only in the paper async mode.
Make sure the view for the link is rendered when you add the link tools. For this please use joint.dia.Paper.prototype.requireView().
// It is fine to use the method in the `sync` mode as the view is rendered/updated
// synchronously as soon as the link is added to the graph.
var linkView = link.findView(paper);
// This method makes sure the view is ready to use in the `async` mode.
// It forces the view to render/update synchronously.
var linkView = paper.requireView(link);
Alternatively, you could pass async = false flag when adding the link to the graph to tell the paper to render the particular view synchronously.
link.addTo(graph, { 'async': false });

CRM JS event for field value changed by workflow

I have form and button on that form in CRM 2015. By clicking on button on form, user triggers on-demand workflow. When workflow is done, it updates value of one of the fields on the form. However, this server data change is not reflected on the user UI.
What is the best way to register JS callback which will refresh form if workflow execution is successful?
Reading this: https://msdn.microsoft.com/en-us/library/gg334701.aspx it looks like I can't use OnChange() event, because I change data programatically.
First of all I would suggest to use Sync Workflow. After workflow is executed just execute following code:
Xrm.Page.data.refresh(false);
I had such requirements once. I had to reflect changes on the Form that are changed by the Async Workflow, and for some reason i had to keep the Workflow Async.
Following is a work-around i did for requirements like this.
Add a new field to the entity, on which the workflow is executed.
FieldName: "isworkflowexecutedsuccessfully"
FieldType: "TwoOption"
Default Value: "false"
Then in your code where you have written the workflow code, write this:
function someFunctionOfYours() {
RunWorkflow(); //
WaitForWorkflowToCompleteProcessingAndThenReload();
}
function isWorklowExecutionCompleted(TimerId, updateIsWorkflowExecutedSuccessfully) {
var entityName = Xrm.Page.data.entity.getEntityName();
var entityGuid = Xrm.Page.data.entity.getId();
var retrievedOpportunity = XrmServiceToolkit.Soap.Retrieve(entityName, entityGuid, new Array("isworkflowexecutedsuccessfully")); //synchronous call
if (retrievedOpportunity.attributes["isworkflowexecutedsuccessfully"].value = true) {
clearInterval(TimerId);
setTimeout(function () {
setIsworkFlowExecutedSuccessfullyToFalse(updateIsWorkflowExecutedSuccessfully);
}, 3000);
}
}
function WaitForWorkflowToCompleteProcessingAndThenReload() {
var TimerId = setTimeout(function () {
isWorklowExecutionCompleted(TimerId);
}, 5000);
}
function setIsworkFlowExecutedSuccessfullyToFalse(updateIsWorkflowExecutedSuccessfully) {
var entityName = Xrm.Page.data.entity.getEntityName();
var entityGuid = Xrm.Page.data.entity.getId();
var updateOpportunity = new XrmServiceToolkit.Soap.BusinessEntity(entityName, entityGuid);
updateOpportunity.attributes["isworkflowexecutedsuccessfully"] = false;
if (updateIsWorkflowExecutedSuccessfully == false || updateIsWorkflowExecutedSuccessfully == null) {
XrmServiceToolkit.Soap.Update(updateOpportunity);
}
Xrm.Utility.openEntityForm(entityName, entityGuid) //refresh form
}

Define a global ready function for every page in WinJS

My WinJS app uses the single navigation model. There is some common code that I would like to apply to every page in the app. Instead of placing the code in each page's ready function, I would like to be able to able to define a "global" ready function that will be executed when a page's ready event is fired. Any ideas?
you can define a Mixin object with utility function used for all pages.
utils.js:
PageMixin = {
ready: function ready(element, options)
{
this.element = element;
this.options = options;
this.initialize();
this.onready();
},
initialize: function initialize()
{
// write common initialize code here
}
};
page.js:
var Page = WinJS.UI.Pages.define('/pages/mypage/page.html',
{
onready: function onready()
{
// page specific initialization code here
}
});
// this will make all PageMixin util methods available on Page.
WinJS.Class.mix(Page, PageMixin);
refer WinJS.Class.mixin for details.

Node.js delegating functions to an imported module

Currently I have a Node.js module with the following form :
var events = require('events');
var emitter = new events.EventEmitter();
function emitSomething() {
emitter.emit("event");
}
exports.emitSomething = emitSomething;
exports.on = emitter.on;
However any callback registered through on do not get called when I call emitSomething.
I can get around this by changing the last line to
exports.on = function(event, callback) { emitter.on(event, callback); };
Is there a reason I can't delegate the on function to a function defined in another module?
This is a common JS mistake. Note that the on method depends on this (which is emitter in your example). However, all you export is the function itself, so it's context (emitter) is lost when you call it later.
Here is a small example for this issue:
var someObj = {
doSth: function() { console.log(this) }
};
someObj.doSth(); // "Object { ..."
var doSth = someObj.doSth;
doSth(); // "Window { ..."
When I call doSth as method of someObj, this references someObj as expected. After I copied the function to doSth, this references it's new context, in the browser the global context Window. This is what happens in your case.
As you already mentioned, you have to bind the emitter context to the function. This can also be done like this:
exports.on = emitter.on.bind(emitter);

Moving from localStorage to chrome.storage

I would like to use chrome.storage API to save the settings of my users instead of localStorage in my Chrome extension.
Currently my options.js (with localStorage and JSON) file looks like this:
$(function(){ //jQuery Ready
// INIT
$("#notifysav").hide();
// STORAGE
if(localStorage['options']){
var o = JSON.parse(localStorage['options']);
$("#option1").attr('checked', o.option1);
$("#option2").attr('checked', o.option2);
.... [list of options]
}
// Save Button Click event
$("#save").live('click', function(){
localStorage['options'] = JSON.stringify({
"option1":$("#option1").attr('checked'),
"option2":$("#option2").attr('checked'),
.... [list of options]
});
// notification
$("#notifysav").fadeIn("slow").delay(2000).fadeOut("slow");
// reload to apply changes
chrome.extension.sendRequest({action: 'reload'});
});
});// #jQuery ready
My question is how to convert my current code to use the chrome.storage API. From what I understand, I would apply those changes:
$(function(){
// INIT
var storage = chrome.storage.sync;
$("#notifysav").hide();
// Load Options
loadOptions();
// Save Button Click Event
$("#save").live('click',function(){ saveOptions(); });
function loadOptions() {
storage.get( /* Something */ )
}
function saveOptions() {
var option1 = $("#option1").attr('checked');
var option2 = $("#option2").attr('checked');
storage.set({"option1":option1,"option2":option2}, function() {
// Notification
$("#notifysav").fadeIn("slow").delay(2000).fadeOut("slow");
// Reload Event to apply changes
chrome.extension.sendRequest({action: 'reload'});
});
}
});
Thanks for your help!
If I understand correctly, your main problem is how to retrieve data from the storage. Here is what can be done:
chrome.storage.local.get(null, function(all){console.log(all)});
will return you an object with all keys and values stored in the storage, in your case it will output:
Object {option1: "value1", option2: "value2"}
Also you can get just one of the keys:
chrome.storage.local.get("optionkey", function(all){console.log(all)});
or an array of keys:
chrome.storage.local.get(["opt1", "opt2"], function(all){console.log(all)});
In any case you can access data in the callback just by key names.
Thanks for your reply. I finally managed to get something working by changing my original code as follows:
$(function(){
// INIT
const storage = chrome.storage.sync;
var options = new Array();
$("#notifysav").hide();
loadOptions();
// STORAGE
// Save Button Click event
$("#save").live('click', function(){ saveOptions(); });
function loadOptions() {
storage.get('options', function(o) {
$("#option1").attr('checked', o.options[0]);
$("#option2").attr('checked', o.options[1]);
...
});
}
function saveOptions() {
options[0] = $("#option1").prop('checked');
options[1] = $("#option2").prop('checked');
...
//console.log(options);
storage.set({'options':options},function(){
$("#notifysav").fadeIn("slow").delay(2000).fadeOut("slow");
chrome.extension.sendRequest({action: 'reload'});
});
}
});

Resources