Call JavaScript function from Drupal module? - drupal

Im a Drupal themer and im struggling to modify a module in the way that I need. When something happens I need to run a very simple bit of JavaScript. Ive found the part of the module responsable and the following works:
if (something = something else) {
return array(
'#commands' => array(
// Hacky by works
ajax_command_append('body',
'<script>
alert("Custom Js");
</script>'),
),
);
}
else {
// Do something else
}
However it would be better to call a function that was somewhere else as other modules will want to call it. How can I call a function from another module from within this statement?

You can do it inline, like this
Edit
In my first answer I forgot to wrap the javascript in jQuery(document).ready(function() {})
module
if (something = something else) {
// Inline fixed
drupal_add_js("jQuery(document).ready(function() { alert('Script inline!'); });", "type" => "inline");
// From file
drupal_add_js("myscript.js", "file");
return array(
'#commands' => array(
),
);
}
else {
// Do something else
}
myscript.js
(function() {
alert('Script from file!');
});
// or with jQuery
(function($) {
// Do something with $
})(jQuery);
If you want the javascript code to be in on its own file, do it like that (taken from the examples)
Check out the examples in the documentation
More information on adding javascript to your modules/themes
Adding Javascript to your theme or module
Managing JavaScript in Drupal 7
Edit 2
You can call a javascript function from a module. I would do it like this :
Create a new module that just inject a javascript code. This module should have a lesser weight than any other module so it executes before any of them.
Then call it from any module using Drupal.settings.
Assuming that you created the module, you should save your javascript function into Drupal.settings
(function ($) {
Drupal.settings.exampleModule = {
myFunction : function () {
alert('My example function!');
}
};
}(jQuery));
This will inject that code into settings and will be available to any module which needs to access it.
Your module
if (something = something else) {
// Inline fixed
drupal_add_js("(function($) { Drupal.settings.exampleModule.myFunction(); })(jQuery);", "type" => "inline");
}
else {
// Do something else
}
That's how I would do it. Now, if you need to use the ajax framework you should do something like this (try it, I've never used this framework before, so I'm guessing)
return array(
'#commands' => array(
// Hacky by works
ajax_command_append('body', "(function($) { Drupal.settings.exampleModule.myFunction(); })(jQuery);"),
),
);

Related

error when i use cy.xpath inside cy.origin

I'm using Cypress to test a website.
When I use xpath inside cy.origin(), It's not working.
it('t1', function() {
cy.origin(('BaseUrl'), () => {
cy.visit('/profile')
cy.xpath("//input[#name='username']").type('user')
cy.xpath("//input[#name='password']").type('pass')
cy.xpath("//button[#type='button']").click()
})
})
Error:
TypeError
cy.xpath is not a function
Note that it works correctly outside cy.origin()
TLDR: Stick with standard Cypress commands inside cy.origin().
It's a current limitation of cy.orgin(), in fact any custom command must be treated specially and cy.xpath() is a custom command.
See Callback restrictions
It is also currently not possible to use require() or dynamic import() within the callback. Because of this limitation, it cannot use npm packages or other third-party libraries inside the callback, as there is no mechanism to reference them. This functionality will be provided in a future version of Cypress.
While third-party packages are strictly unavailable, it is possible to reuse your own code between cy.origin() callbacks. The workaround is to create a custom Cypress command within the secondary origin in a before block:
before(() => {
cy.origin('somesite.com', () => {
Cypress.Commands.add('clickLink', (label) => {
cy.get('a').contains(label).click()
})
})
})
it('clicks the secondary origin link', () => {
cy.origin('somesite.com', () => {
cy.visit('/page')
cy.clickLink('Click Me')
})
})
But you can't use this pattern with cy.xpath() as you currently need to require('cypress-xpath') and that can't be done inside cy.origin().
A Workaround
Navigate to /node_modules/cypress-xpath/src/index.js
Copy the entire contents
Add a new command file in support: /cypress/support/xpath.js
Add this to the file, pasting in the copied code
before(() => {
cy.origin('somesite.com', () => { // your cross-origin URL here
// paste here code from /node_modules/cypress-xpath/src/index.js
})
})
Import xpath.js into /cypress/support/commands.js
Now cy.xpath() will work within cy.orgin() in any of your tests.
Check to see if non-xpath works.
cy.origin(('BaseUrl'), () => {
cy.visit('/profile')
cy.get("input[#name='username']").type('user')
...
If not, you've probably not set the experimentalSessionAndOrigin flag correctly.

How to have Cypress go through every page on site to see if there are any console errors and if so, make it known to the user running the test

I want Cypress to go through every page to see on a website to see if there are any console errors and if so, make it known to the user running the test. (I'm thinking it would be useful for CSP checking to see if the site is throwing a console error because of a domain not being whitelisted.)
This package cypress-fail-on-console-error
may make it easier
test
import failOnConsoleError from 'cypress-fail-on-console-error';
failOnConsoleError();
const pages = [ "/page1", "/page2" ]
pages.forEach(page => {
it(`verifies the page ${page}`, () => {
cy.visit(page)
})
})
There's some interesting stuff on Cypress and CSP here
Testing Content-Security-Policy using Cypress ... Almost
You can use a combination of Cypress functionality to achieve this. You could store the list of links in an array of strings, use Cypress Lodash to iterate through each string as a separate test, and use the onBeforeLoad callback within cy.visit() to spy on console.error.
describe('Tests', () => {
// Define links
const links = ['/1', '/2', '/3'...]
// Iterate through the links array using Cypress Lodash
Cypress._.times(links.length, (index) => {
it('validates site loads with no errors', () => {
cy.visit(links[index], {
// set the `onBeforeLoad` callback to save errors as 'error'
onBeforeLoad(win) {
cy.stub(win.console, 'error').as('error');
}
});
// Validate error was not called
cy.get('#error').should('not.have.been.called');
});
});
});
A good deal of this answer was taken from this answer.
If you'd like to be specific about the errors that fail, try catching uncaught:exception
Cypress.on('uncaught:exception', (err) => {
if (err.message.includes('Content Security Policy')) {
return true
} else {
return false // only fail on the above message
}
})
describe('Testing Content Security Policy', () => {
const pages = [ "/page1", "/page2" ]
pages.forEach(page => {
it(`visiting page ${page}`, () => {
cy.visit(page)
})
})
})

Override saveCompat for WordPress Media Uploader

I am new to Backbone, so can't understand what I am doing wrong. What I need is to override saveCompat (media-models.js line 310, WP 3.9.1). I am trying to do it same way I have overridden some media views
wp.media.model.Attachment = wp.media.model.Attachment.extend({
saveCompat: function( data, options ) {
some code here...
}
});
But it doesn't work for me. Native WordPress saveCompat is executed. At the same time the very same idea is perfectly working for wp.media.view.AttachmentCompat for example
wp.media.view.AttachmentCompat = wp.media.view.AttachmentCompat.extend({
save: function( event ) {
some code here...
}
});
Thanks in advance!
I figured it out. The correct way to extend it is:
_.extend( wp.media.model.Attachment.prototype, {
saveCompat: function( data, options ) {
some code here...
}
});
Using _.extend removes the ability to call the super class implementation of methods. The proper (or at least a working) way to do it is to use Backbone's extend method and then overwrite the prototype in wp.media.model.Attachment like this:
var MediaCreditAttachmentModel = wp.media.model.Attachment.extend( {
saveCompat: function( data, options ) {
...
}
} );
wp.media.model.Attachment.prototype = MediaCreditAttachmentModel.prototype;

dynamically inserting templates in meteor

Ok so I've got my template in its own file named myApp.html. My template code is as follows
<template name="initialInsertion">
<div class="greeting">Hello there, {{first}} {{last}}!</div>
</template>
Now I want to insert this template into the DOM upon clicking a button. I've got my button rendered in the DOM and I have a click event tied to it as follows
Template.chooseWhatToDo.events = {
'click .zaButton':function(){
Meteor.ui.render(function () {
$("body").append(Template.initialInsertion({first: "Alyssa", last: "Hacker"}));
})
}
}
Now obviously the $("body").append part is wrong but returning Template.initialInsertion... doesn't insert that template into the DOM. I've tried putting a partia {{> initialInsertion}}but that just errors out because I dont have first and last set yet... any clues?
Thanks guys
In meteor 1.x
'click .zaButton':function(){
Blaze.renderWithData(Template.someTemplate, {my: "data"}, $("#parrent-node")[0])
}
In meteor 0.8.3
'click .zaButton':function(){
var t = UI.renderWithData(Template.someTemplate, {my: "data"})
UI.insert(t, $(".some-parrent-to-append"))
}
Is first and last going into a Meteor.Collection eventually?
If not, the simplest way I know is to put the data into the session:
Template.chooseWhatToDo.events = {
'click .zaButton' : function () {
Session.set('first', 'Alyssa');
Session.set('last', 'Hacker');
}
}
Then you would define:
Template.initialInsertion.first = function () {
return Session.get('first');
}
Template.initialInsertion.last = function () {
return Session.get('last');
}
Template.initialInsertion.has_name = function () {
return Template.initialInsertion.first() && Template.initialInsertion.last();
}
Finally, adjust your .html template like this:
<template name="initialInsertion">
{{#if has_name}}
<div class="greeting">Hello there, {{first}} {{last}}!</div>
{{/if}}
</template>
This is the exact opposite solution to your question, but it seems like the "Meteor way". (Basically, don't worry about manipulating the DOM yourself, just embrace the sessions, collections and template system.) BTW, I'm still new with Meteor, so if this is not the "Meteor way", someone please let me know :-)
I think you may want to use Meteor.render within your append statement. Also, note that if you are passing data into your Template, then you must wrap Template.initialInsertion in an anonymous function, since that's what Meteor.render expects. I'm doing something similar that seems to be working:
Template.chooseWhatToDo.events = {
'click .zaButton':function(){
$("body").append(Meteor.render(function() {
return Template.initialInsertion({first: "Alyssa", last: "Hacker"})
}));
}
}
Hope this helps!
Many answer here are going to have problems with the new Blaze engine. Here is a pattern that works in Meteor 0.8.0 with Blaze.
//HTML
<body>
{{>mainTemplate}}
</body>
//JS Client Initially
var current = Template.initialTemplate;
var currentDep = new Deps.Dependency;
Template.mainTemplate = function()
{
currentDep.depend();
return current;
};
function setTemplate( newTemplate )
{
current = newTemplate;
currentDep.changed();
};
//Later
setTemplate( Template.someOtherTemplate );
More info in this seccion of Meteor docs

drupal Select cck onchange handler

i'm adding onchange handler to all cck fields by following manner.
function bday_form_event_node_form_alter(&$form, &$form_state) {
$form['title']['#attributes'] = array('onchange' => "titleval()");
$form['#after_build'][] = 'bday_form_event_node_form_cck_alter';
}
function bday_form_event_node_form_cck_alter($form, &$form_state) {
$form['field_date1'][0]['value']['#attributes'] = array('onchange' => "dateval()"); //Text Field
$form['field_city']['#attributes'] = array('onchange' => "cityval()"); //Select Field
}
But Onchange handler for Select is not added to the dom .
Building a little on what googletorp said.
This is not the way to add behaviours to forms in drupal.
There is a very nice JS api that comes with drupal which can aid you in doing this.
Roughly what you would want is something like this.
Drupal.behaviors.myModuleBehavior = function(context) {
$('.title').change(function() {titleval() }) ;
$('.field_date1').change(function() {dateval()});
$('.field_city').change(function() {cityval()});
}

Resources