I am trying to copy some data from one processed web page into a new one that I want to export. The background is that I need to scrape parts of a page and need to build a new page with parts of the original page.
The problem seems that phantomJs includeJs() and evaluate() methods are sandboxed and I can't see a proper way to import DOM from one page to another.
I have some test code that looks like this, with page being the original and out the new page:
....
var title = page.evaluate(function() {
return title = document.getElementById('fooo').innerHTML;
});
console.log('page title:' + title);
//fs.write('c:/Temp/title.js', "var title = '" + title + "';", 'w');
var out = new WebPage;
out.viewportSize = page.viewportSize;
out.content = '<html><head></head><body><div id="wrapper"></div><p>done</p></body></html>';
out.includeJs('c:/Temp/title.js', function() {
var p = document.createElement('p');
p.appendChild(document.createTextNode(title));
document.getElementById('wrapper').appendChild(p);
});
...
The function in your last includeJs call here won't work - as you note, it's sandboxed, and that means that closures won't work, so title won't be defined. A method of passing variables to page.evaluate is noted as a feature request, but isn't available as of PhantomJS v.1.4.1.
The general way I get around this is by using the Function constructor, which allows you to create a function using a string:
var myVar = {some:"values", I:"want to pass into my page"},
test = new Function("window.myVar = " + JSON.stringify(myVar));
page.evaluate(test);
Now you can evaluate a function like the one you have, referencing myVar in the sandbox, and your data will be available in the client scope.
Related
I'm writing some code where I need to be able to load, unload, and reload modules on the fly (without closing the program). A big issue I'm running into is that there's no way to give unique names to the modules when I reload them - that is, I'm using a generalized function to reload them:
try {
delete require.cache['./plugins/'+plugin];
console.log("Reloading " + plugin + ".");
var ??? = require('./plugins/'+plugin);
}
Given that this needs to work for an unspecified number of modules (and even modules that weren't included upon startup), how can I go about naming the new Require variable (currently ??? is a placeholder).
Thanks!
edit:
for (i in fs.readdirSync("./plugins")) {
var pluginName = plugins[i].split('.')[0]
plugins[pluginName] = pluginName;
plugins[pluginName] = require("./plugins/" + pluginName);
console.log(global.plugins[i].split('.')[0]);
};
console.log(testModule1.addi(2, 3));
console.log(testModule2.mult(2,3));
This is what I have right now and it doesn't work (testModule1.addi is just addition and testModule2.mult is just mulitplication). It works just fine when I do something like:
var testModule1 = require("./plugins/testModule1");
var testModule2 = require("./plugins/testModule2");
and then call the functions.
You can't have variable variable names like that, but fortunately you can use an object, and give it property names which are variable, something like this:
var uuid = require('node-uuid');
var myModules = {};
var moduleName = 'module_' + uuid.v1();
myModules[moduleName] = require('./plugins/' + plugin);
This will guarantee you a unique name each time under moduleName that you can use.
I have a strange issue and I have been stuck for hours now. I am working on porting somebody's game to a social platform called Mixi. Now, there is a button inside the game which triggers a Javascript function with four parameters. (the game passes the values for these while calling.)
Now, I have written definition for this function calling the Mixi API for payment and if I call this function from anywhere within my file, the window/widget shows up. But, when it gets called from within the game, something messes up and it just crashes.
I have checked Console and it seems that something is wrong with the context or scope. If I put an alert in the same function, it gets called perfectly fine from within the game. But, since it is an API call and depends on objects from other JS files, included before this file in the Mixi application, I think opensocial.requestPayment malfunctions since context changes when function is called from within the game.
Same thing is observed for functionality like Inviting friends. A call from within the .js or another .js in the same application works fine but from within the game API call does not work, and the Network monitor in Firebug shows "rpc.json" something as the last thing. The same thing also comes when the function is called manually, but is immediately followed by something win_create.pl from the server of Mixi and shows up the dialog.
Code for the functions seems to be fine, since it works perfectly when called from this container web application. But from within the game, only the alerts come (means function is called) but API is not called correctly.
I have tried doing this:
var that = this; // outside the function
function sendPaymentRequest(itemName, amount etc.){
that.opensocial.sendPaymentRequest... // works only if called from within this application
}
But this makes no difference, either.
I am sorry if this sounds confusing, as I didn't know how to explain this in a nutshell. If you need any further information, please let me know.
Update: Code Snippets:
The following function is used for calling opensocial's payment request:
function sendPaymentRequest(id, price, count, name, imageUrl) {
var itemId = id;
var amount = price * count;
var itemName = name;
$.post(
"http://url.com/payment.php",
{"item_id":itemId, "item_price":amount, "item_name":itemName },
function(data){
var responseJSON = JSON.parse(data);
//alert("Signature Fetched: "+ responseJSON.signature); -- this works
var params = {};
params[opensocial.Payment.Field.AMOUNT] = amount;
params[mixi.Payment.Field.ITEM_NAME] = "diamond";
params[mixi.Payment.Field.SIGNATURE] = responseJSON.signature;
params[mixi.Payment.Field.ITEM_ID] = id;
params[mixi.Payment.Field.IS_TEST] = 'true';
params[mixi.Payment.Field.INVENTORY_CODE] = 123;
params[opensocial.Payment.Field.PAYMENT_TYPE] = opensocial.Payment.PaymentType.PAYMENT;
var payment = opensocial.newPayment(params);
//alert("Just before opensocial request payment" + opensocial.requestPayment); -- works even when called from game
console.log(payment);
opensocial.requestPayment(
payment,
"http://url.com/payment-callback.php",
function(response){
var data;
var code;
var msg;
if (response.hadError()){
code = response.getErrorCode();
msg = response.getErrorMessage();
alert('code=' + code + ' msg=' + msg);
}else{
data = response.getData();
// alert('ok. itemNmae=' + data.fields_.itemName);
}
}
);
});
}
This specific function is being called from within the game. But, the window for payment which this method is supposed to open doesn't show. However, if the same call to opensocial.requestPayment is made from anywhere within the javascript file from a global scope, it shows instantly.
I am using jQuery to GET the contents of a navigation file and load it into a div on part of my page.
I am supplying it a value containing the page name so I can update the "active" class on the navigation.
Here is the code I am using to show the GET information, this works flawlessly as when I pass it a variable from my browser (i.e header.html?page=foo) it works perfectly.
<script>
function getQueryParams(qs) {
qs = qs.split("+").join(" ");
var params = {},
tokens,
re = /[?&]?([^=]+)=([^&]*)/g;
while (tokens = re.exec(qs)) {
params[decodeURIComponent(tokens[1])]
= decodeURIComponent(tokens[2]);
}
return params;
}
var $_GET = getQueryParams(document.location.search);
alert($_GET["page"]);
</script>
As you can see at the moment it just shows an alert with the value.
When I am calling this page, It is as simple as:
<script>
$("#header").load("header.html?page=bar");
</script>
For the life of me I cannot understand why it is coming back as "undefined" when called from the .load, as I have used it many times before with GET variables.
Any input would be much appreciated,
Kris.
When Javascript is being run by .load, the document is the original document, not the page being loaded.
To pass parameters to the script being loaded, use a global variable.
I am currently using a JS script called Timeframe.js with the Prototype javascript framework, to create a calendar widget. In my local copy it works great, however in my staging environment I am getting the following javascript error:
TypeError: this.element.down(...) is undefined
this.element.down('div#' + this.element.id + '_container').insert(calendar);
The error is referring to line 97 in timeframe.js library file. It is a part of the following method:
// Scaffolding
createCalendar: function() {
var calendar = new Element('table', {
id: this.element.id + '_calendar_' + this.calendars.length, border: 0, cellspacing: 0, cellpadding: 5
});
calendar.insert(new Element('caption'));
var head = new Element('thead');
var row = new Element('tr');
this.weekdayNames.length.times(function(column) {
var weekday = this.weekdayNames[(column + this.weekOffset) % 7];
var cell = new Element('th', { scope: 'col', abbr: weekday }).update(weekday.substring(0,3));
row.insert(cell);
}.bind(this));
head.insert(row);
calendar.insert(head);
var body = new Element('tbody');
(6).times(function(rowNumber) {
var row = new Element('tr');
this.weekdayNames.length.times(function(column) {
var cell = new Element('td');
row.insert(cell);
});
body.insert(row);
}.bind(this));
calendar.insert(body);
this.element.down('div#' + this.element.id + '_container').insert(calendar);
this.calendars.push(calendar);
this.months = this.calendars.length;
return this;
},
I've also verified that the contents of this.element.id are valid. The value ends up being 'div#calendars_container' which is an element that exists within the markup, so it's not missing from the DOM.
I've been unable to determine the root of this issue, I've ensured I have the latest version of Prototype and Timeframe but the error still occurs. I've been using these scripts in this way for some time now and haven't run into this before now, can anyone put me on the right track?
You will need to share a little more context to get this resolved, I think. If the code runs fine locally but not on your (presumably off-site) staging environment, then it could be a matter of not all of your code being loaded by the time execution reaches the line in question.
I appears that your snippet is inside an event handler; even if all your code is loaded prior to reaching this line, perhaps a timing issue exists where Prototype hasn't yet extended the this.element object with the down() function?
Most likely the greater latency between your staging environment is causing some network-bound function to execute after another, in a way opposite to how it routinely does it for you locally.
Put a breakpoint on this line and see what happens.
Is there a way to get the ScriptNode that initiated a state in an Activiti workflow in Alfresco? I have a ScriptTask in my workflow, and it has a Alfresco Script Listener set up for the Start event. When the script is called, I'd like the ScriptNode that transitioned into the ScriptTask in the workflow to be passed as a parameter to the function defined as the listener. Is that possible?
Editing for clarity:
Here's a screenshot of Eclispe with the Activiti plugin.
http://i.imgur.com/DAKtq.jpg
This workflow gets started by another workflow with webscripts.
var props = new Object();
var dd = new Date();
props[EL_PROP_WORK_UNIT_NAME] = "testNode" + DateISOString( dd );
props[EL_PROP_WORK_UNIT_SOURCE_CODE] = "ROB";
props[EL_PROP_WORK_UNIT_DELIVERY_DATE] = dd;
node = getHome().createNode(name, EL_TYPE_WORK_UNIT, props);
var EL_WORKFLOW = "activiti$The-workflow";
var activeWfs = node.activeWorkflows;
if( activeWfs === null || activeWfs.length === 0 )
{
var workflowPackage = workflow.createPackage();
workflowPackage.addNode( node );
var workflowDef = workflow.getDefinitionByName(EL_WORKFLOW);
var workflowPath = workflowDef.startWorkflow( workflowPackage, new Object());
}
So the listener calls another javascript method...
function artPDFRename()
{
logger.log("==============================");
logger.log("<START> artPDFRename");
var workflowDef = workflow.getDefinitionByName(EL_WORKFLOW);
var activeInstance = workflowDef.getActiveInstances();
// ????
}
The goal is to have this handling be automatic. We're trying to design this with as little of manual intervention as possible, and are not assigning tasks to users to perform. Yes, there's probably another way to rename a PDF file, but I can't seem to figure out from the documentation listed here how to get a pointer to the node I put in the bpm_package object. That's the question.
Or am I so far off base on how we're developing this that it makes no sense?
As an example check the ScriptTaskListener class. Here all the workflow variables are put in a map.
The following code is interesting:
// Add all workflow variables to model
Map variables = delegateTask.getExecution().getVariables();
for (Entry<String, Object> varEntry : variables.entrySet())
{
scriptModel.put(varEntry.getKey(), varEntry.getValue());
}
So with this you could use bpm_package as an object within your script within the workflow script task.
So if you need the node were the workflow has run on, the following code should work (where task is your delegateTask from your notify method of the Listener:
delegateTask.getVariable("bpm_package");
// or like the example above
delegateTask.getExecution().getVariable("bpm_package");
This will be a list so take the first one and that will be your node.
---------update
If you're using the javascript from alfresco then you can directly use the parent object bpm_package.
So in your case it would be best to do the following:
var node = bpm_package.children[0]; //or you could check if the
package isn't null
// Then send the node into your
artPDFRename(node); //or you could just add the bpm_package code in
your js file