I would like to register a listener in chrome.webRequest API, like in the following JS example:
var initHttpRequestObserver = function () {
chrome.webRequest.onBeforeSendHeaders.addListener(
function (details) {...},
{urls: ["<all_urls>"]},
["blocking", "requestHeaders"]);
}();
I guess I could use the dart:js, but wanted to use the chrome package and save some time/typing:
Stream<Map> aStream = chrome.webRequest.onBeforeSendHeaders;
Unfortunately, I am not able to find out how to supply the mandatory filter and opt_extraInfoSpec arguments.
After some analyses of the chrome package, it seems like it is not foreseen to invoke addListener with more then one parameter (a callback). The common.dart contains the private method where the actual invocation is being done:
void _ensureHandlerAdded() {
if (!_handlerAdded) {
// TODO: Workaround an issue where the event objects are not properly
// proxied in M35 and after.
var jsEvent = _api[_eventName];
JsObject event = (jsEvent is JsObject ? jsEvent : new JsObject.fromBrowserObject(jsEvent));
event.callMethod('addListener', [_listener]);
_handlerAdded = true;
}
}
Obviously event.callMethod('addListener', [_listener]); is not providing additional parameters.
The "official" chrome package requires a fix. In the meantime one can use the old good dart:js and do the following:
JsObject _OnBeforeSendHeaders = context['chrome']['webRequest']['onBeforeSendHeaders'];
var filter = new JsObject.jsify({"urls": ["<all_urls>"]});
var opt_extraInfoSpec = new JsObject.jsify(["blocking", "requestHeaders"]);
_OnBeforeSendHeaders.callMethod('addListener', [_processCallback, filter, opt_extraInfoSpec]);
Related
I bought a 3rd party google app script to use. However, it can only be called with onEdit method and their codes are private that i cannot make change. Also, what i need is based on time-trigger instead of onEdit-trigger. Thus, I tried to build my own event to trigger the function:
// This function work well and can call the 3rd Party App Script
// It is triggered by onEdit googlesheet, which works well
function funcOnEdit(e) {
3rdPartyApp.funcOnEdit(e));
}
// Below is the jsontostring result of the event e
// {authMode:"FULL",oldValue:"false",range:{columnEnd:6,columnStart:6,rowEnd:3,rowStart:3},source:{},triggerUid:"xxx",user:{email:"xxxx#gmail.com",nickname:"xxxx"},value:"TRUE"}
So I build a similar event object which triggered by time to make it happened.
function funcOnTimeTrigger(e) {
var e1 = {authMode:"FULL",oldValue:"false",range:{columnEnd:6,columnStart:6,rowEnd:3,rowStart:3},source:{},triggerUid:"xxx",user:{email:"xxxx#gmail.com",nickname:"xxxx"},value:"TRUE"};
e1.triggerUid = e.triggerUid;
3rdPartyApp.funcOnEdit(e1));
}
Unfortunately, I cannot find any document and reference code to build an "onEdit" event. Thats why, I tried find the object/class myself.
function getObjType(obj) {
var type = typeof(obj);
if (type === "object") {
try {
// Try a dummy method, catch the error
type = obj.getObjTypeXYZZY();
} catch (error) {
// Should be a TypeError - parse the object type from error message
// type = error.message.split(" object ")[1].replace('.','');
type = error.message;
}
}
return type;
}
// Below function is triggered by onEdit
function funcOnEdit_checker(e) {
getObjType(e);
}
// Unfortunately, it cannot show the object name or classname
I have no idea what to do next, may i know if it is possible to build an event class/object ourselves in Google Script App? Can anyone give some hints on how to do so? or it is not possible?
I want to create the event-obj "developers.google.com/apps-script/guides/triggers/events" manually and pass the event "e" to 3rdPartyApp.funcOnEdit function. Is it possible to do so?
Reference:
https://developers.google.com/apps-script/guides/triggers/installable
Thanks #Cooper idea, who share same thought as me.
And finally I found the result # Explain purpose of `e` event parameter in onEdit
Below is my answer (not yet optimized but work):
function funcOnTimeTrigger(e) {
var e2 = {}
e2["authMode"] = ScriptApp.AuthMode.FULL
e2['user'] = "me";
e2['range'] = SpreadsheetApp.getActive().getSheetByName("XXXXXX").getRange(5,3).activate();
e2.range.columnStart = 5;
e2.range.columnEnd = 5;
e2.range.rowStart = 3;
e2.range.rowEnd = 3;
e2['source'] = SpreadsheetApp.getActive();
e2['oldValue'] = "old";
e2['value'] = "new";
// Run The Program
3rdPartyApp.funcOnEdit(e2);
}
In Meteor (a NodeJS Framework), there is a function called Meteor.userId() that always returns the userId that belongs to the current session as long as I am in a function that was original called from a Meteor Method.
The Meteor.userId() function utilizes meteors DDP?._CurrentInvocation?.get()?.connection. So somehow this "Magic line" gets my current DDP connection. This also works when burried deep inside of callbacks.
So somehow meteor sets a context that it refers to. I also want to do this kind of trick for another API that doesn't utilize meteors DDP but is a plain HTTP Api.
What I want to do:
doActualStuff = function(param1, param2, param3) {
// here, i am burried deep inside of calls to functions
// but the function at the top of the stack trace was
// `answerRequest`.
// I want to access its `context` here but without
// passing it through all the function calls.
// What I want is something like this:
context = Framework.getRequestContext()
}
answerRequest = function(context) {
//do some stuff
someFancyFunctionWithCallback(someArray, function(arrayPosition) {
aFuncCallingDoActualStuff(arrayPosition);
})
}
I can wrap the call to answerRequest if this is necessary.
I don't know how Meteor does it, but it doesn't look like magic. It looks like Meteor is a global object (window.Meteor in the browser or global.Meteor in Node.js) that has some functions that refer to some stateful object that exists in the context where they were defined.
Your example could be achieved by having answerRequest (or whatever function calls answerRequest, or whatever you want) call a setRequestContext function that sets the state that will be returned by getRequestContext. If you wanted, you could have an additional function, clearRequestContext, that cleans up after request is over. (Of course, if you have async code you'll need to take care that the latter isn't called until any code that needs that data has finished running.)
This is rudimentary, but it might look something like the below snippet. window.Framework does not need to be defined in the same file as the rest of the code; it just needs to be initialized before answerRequest is called.
let _requestContext = null;
window.Framework = {
setRequestContext(obj) {
_requestContext = obj;
},
getRequestContext() {
return _requestContext;
},
clearRequestContext() {
_requestContext = null;
},
};
const doActualStuff = function(param1, param2, param3) {
const context = Framework.getRequestContext()
console.log('Request context is', context);
}
const answerRequest = function(context) {
Framework.setRequestContext(context);
setTimeout(() => {
try {
doActualStuff();
} finally {
Framework.clearRequestContext();
}
}, 100);
}
answerRequest({ hello: 'context' });
.as-console-wrapper{min-height:100%}
Rather than copy and pasting my code onto here, I have uploaded it to github. The RequireJS module does have a dependency on jquery.signalr and in tern has a dependency on jquery but also have a dependency on the javascript held in /signalr/hubs. There is a bit of config to do with Require.Config.
Basically what is happening is on the first time you load the page the connection is made to the hubs within signalr and the "server side" code is executed and does the desired thing. When you refresh the page it does not. All client side code is called, so for example:
var myViewModel = new MyViewMode();
myViewModel.init();
and within your init method you have
var connection = $.connection.myHub;
this.init = function() {
connection.server.myMethod();
}
this would then go off to
public MyHub : Hub
{
public void MyMethod()
{
Client.Request.populateSomeInformation() // I think it's request but I'm doing this from memory!
}
}
and then call
connection.client.populateSomeInformation = function () { .. )
but doesn't call this :(
It looks like a connection has been made (using the good old console.log() to see what it outputs) and indeed debugging the project it executes the code within the hub but there is no response made back to the javascript.
So wonderful people of the internet, where am I going wrong? Do I need to check the state of $.connection.hub.start(); before attempting to start it again?
Time for beer :)
I believe it should be
connection.client.populateSomeInformation = function () { .. )
(not connection.server)
http://www.asp.net/signalr/overview/hubs-api/hubs-api-guide-javascript-client#callclient
(observations on the code you have on github right now)
var isLoaded = false;
// ... some code that doesn't change isLoaded ...
if (isLoaded == false) {
scrollIntervalId = window.setInterval(function () {
signalRLoaded();
}, 30);
}
I think isLoaded will always be false at this point. Not sure what you intended this to accomplish.
var connection = $.connection.hub.start();
I don't think you're supposed to open the connection before defining any client functions. I don't see any client functions being defined here, so maybe you're doing that somewhere else? I don't know if it really matters other than if the server attempts to call a client function that hasn't yet been defined...
function SignalRReady(callback) {
if (isLoaded) {
callback(connection);
} else {
readyCalls = callback;
}
return SignalRReady;
}
SignalRReady.version = "1.0.0";
SignalRReady.load = function(name, request, onLoad, config) {
if (config.isBuild) {
onLoad();
} else {
SignalRReady(onLoad);
}
};
return SignalRReady;
I'm confused by this bit of code, probably because I don't see how it's being used. Is this an attempt at a kind of singleton? I see that SignalRReady is the "class" being returned for the module. You're not really returning an object, you're returning a constructor which implies that you're instantiating it in other places, something like
define(['SignalRReady'], function(sigR)
{
var srr = new sigR();
});
But then you have that load function defined that calls the constructor and makes this look all weird. How are you using this?
Anyways, I'm thinking you might be hitting some kind of race condition where the client function may not always be available at the time the server is trying to call it.
(additional comments/code 2013-09-06)
Your connection object is actually a jQuery promise ( http://api.jquery.com/category/deferred-object/ ).
If you're unfamiliar with promises, think of them generically as a queue of callbacks to be executed later. In this case, when connected, all the callbacks will be executed (in the order they were added). If a callback is added after being connected, it will get executed immediately. This is how your code is working now. You add the callback to the .done queue after the connection is made and is executed immediately.
If you insist on creating the connection object yourself, then you do not need to use the stateChanged event. You just add the callback to the .done queue:
define(function()
{
function signalRReady(callback)
{
if (window.connection == undefined) {
window.connection = $.connection.hub.start();
}
window.connection.done(callback);
}
signalRReady.version = "1.0.0";
return signalRReady;
});
However, I believe it's not a good idea to initiate the connection yourself. Because your module isn't a complete wrapper around SignalR such that people would only use your module to do SignalR stuff, you are not guaranteed (and cannot expect) other code will not initiate the connection. Especially if someone is adding your module to an existing codebase.
Your module is simply adding a new event, so keep it simple. Take the callback and execute it yourself when appropriate:
define(function()
{
function signalRReady(callback)
{
$.connection.hub.stateChanged(function (state)
{
if(state.newState === $.signalR.connectionState.connected)
{
callback();
}
});
}
signalRReady.version = "1.0.0";
return signalRReady;
});
Nowadays, promises are pretty popular. You might want to implement a promise-based module like:
define(function()
{
var deferred = $.Deferred();
$.connection.hub.stateChanged(function (state)
{
if(state.newState === $.signalR.connectionState.connected)
{
// executes all callbacks attached by the "ready" function below
deferred.resolve();
}
});
return {
ready: function(callback)
{
deferred.done(callback);
},
version: "1.0.0"
};
});
If callbacks are attached after the connection has been made, they are executed immediately.
Also, notice this example module's init function returns an object instead of a function. Since RequireJS will pass the same instance around to any module that requires it, state is maintained - we can use local variables instead of global.
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.
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);