I have some input values contained within a bunch of divs and a form. I have a limited knowledge of js and would like to use greasemonkey to set the values when the page loads.
The following shows the order of divs and forms to get to the value.
<div data-role="page" id="finalTest">
<div data-role="content">
<form class="cmxform" id="ftForm">
<div id="finalTestForOtherDiv" >
<div data-role="fieldcontain" class="special-spacing">
<div class="input-div">
<label id="finalTestBeforeAcTgrLabel" for="finalTestBeforeAcTg" data-insight="true"
class="mandatory" data-default="mandatory"><img class="label-image" alt="" src="../Images/icon-alert.png" />AC V (T/G):</label>
<input type="text" id="finalTestBeforeAcTg" name="finalTestBeforeAcTg" value="" style="width:100px; display:inline-block;"
data-bind="binder.BeforeAcTg" data-bind-action-when-hidden="Ignore" />
<span class="ui-content" style="padding-left:7px;">V</span>
<div class="error-message"><ul></ul></div>
</div>
Here is the script I've tried using but does nothing.
// ==UserScript==
// #name Final Test
// #description Final test results
// #include https://techaccess.ad.qintra.com/WorkJobs/WorkJobs.aspx#finalTest
// ==/UserScript==
document.getElementById("finalTestBeforeAcTg").value = "0.00";
document.getElementById("finalTestBeforeAcTg").value = "your value";
If your script attempt is not working, it is likely because one or more of the following:
The #include is too restrictive.
The node does not exist yet. Is AJAX active on the page?
Prerequisite javascript events must be triggered for the page to acknowledge or accept the new value.
For issue 1, change the script to:
// ==UserScript==
// #name Final Test
// #description Final test results
// #include http://techaccess.ad.qintra.com/WorkJobs/*
// #include https://techaccess.ad.qintra.com/WorkJobs/*
// ==/UserScript==
alert ("Script start.");
var finTestInput = document.getElementById ("finalTestBeforeAcTg");
if (finTestInput) {
finTestInput.value = "0.00";
}
else {
alert ("The target input does not exist yet!")
}
Note that if you use Firebug (you should), change the alert()s to unsafeWindow.console.log()s.
For issue 2, use the waitForKeyElements utility. See this answer, for example.
For issue 3, There is little we can do without access to the page to suss out the proper sequence of events.
Give us access to the page, or Use Firebug (and the Events panel) to figure it out, or maybe a snapshot of both the page's HTML, and the page's JS files, at Pastebin.com will be enough (maybe).
Related
I'm a beginner is learning how to code for Tampermonkey.
I know how to click an automated clicking using a class tag or id tag.
But is there anywhere to automate a clicking based on data info such as data-id, name or image URL?
The HTML is like:
<div class="yellow-bot" data-image="//imgurl1.gif" data-id="123" data-name="Image1">...</div>
<div class="yellow-bot" data-image="//imgurl2.gif" data-id="124" data-name="Image2">...</div>
<div class="yellow-bot" data-image="//imgurl3.gif" data-id="125" data-name="Image3">...</div>
...
...
...
<div class="submitButton">
<input id="button" type="submit" value="Submit Now" class="btn-primary">
</div>
So I'd like the click to click id 124 and 125 but the class is all the same. and then click on the Submit button. Can anyone help me with this?
Reference CSS selectors and jQuery selectors.
So, selecting by attribute:
document.querySelector (".yellow-bot[data-id='124']").click ();
document.querySelector (".yellow-bot[data-id='125']").click ();
Or much more robustly:
// ==UserScript==
// #name _Click nodes by attribute
// #match *://YOUR_SERVER.COM/YOUR_PATH/*
// #require https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
// #require https://gist.github.com/raw/2625891/waitForKeyElements.js
// #grant GM_addStyle
// #grant GM.getValue
// ==/UserScript==
//- The #grant directives are needed to restore the proper sandbox.
/* global $, waitForKeyElements */
waitForKeyElements (".yellow-bot[data-id='124']", clickNode, true);
waitForKeyElements (".yellow-bot[data-id='125']", clickNode, true);
function clickNode (jNode) {
var clickEvent = document.createEvent ('MouseEvents');
clickEvent.initEvent ('click', true, true);
jNode[0].dispatchEvent (clickEvent);
}
For the rest, see Choosing and activating the right controls on an AJAX-driven site.
there is this piece of code that provides a checkbox following from a link to the T&C.
<div class="checkbox accept_agreement">
<label class="control-label" for="step_data_accept_agreement">
<input type="hidden" name="step_data[accept_agreement]" value="0">
<input type="checkbox" name="step_data[accept_agreement]" id="step_data_accept_agreement" value="1">
<label for="step_data_accept_agreement">
<a target="_blank" href="/lanevillkor/">
<font><font>I agree, have read and stored the terms and conditions</font></font>
</a>
</label>
</label>
</div>
Now, I am working on a spock test using geb, and I try to retrieve the checkbox element
<input type="checkbox" name="step_data[accept_agreement]" id="step_data_accept_agreement" value="1">
to do so i have tried many things without the expected output. i was expected that something like
$("#step_data_accept_agreement").click() would be pretty straight forward but it is not. in the other side if I put $("[for='step_data_accept_agreement'] label").click() it clicks the link.
I tried to become as more specific but nothing looks to return the element correctly.
one of my last attempts was
termsAndConditionsOption(wait: true) { $("#step_data_accept_agreement", name: "step_data[accept_agreement]") }
and the error message, as in the other cases too, was in one sentence
element not visible
What do I miss?
So the solution was to use js to click the checkbox. The simpliest way is:
def agreeOnTermsAndConditionsAccept() {
String mouseClickEvt = """
var evt = new MouseEvent('click', {
bubbles: true,
cancelable: true,
view: window
});
arguments[0].dispatchEvent(evt);
"""
browser.js.exec(termsAndConditionsOption.firstElement(), mouseClickEvt)
}
Geb provides js support to work with javascript, as we find in the documentation. Also another link shows how to simulate a click event with pure javascript.
This was required as the checkbox cant be found as it is hidden. With javascript we can 'see' the position of the element and perform an action in the location that we want. iniMouseEvent can be used as well as it is documentanted here
i have an Angular Storefront app set up. I have a shopping cart functionality in place and a stripe "pay with card" button etc. pretty much looks like this:
<form action="/#/order" method="POST">
<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="{{ stripeApiKey }}"
data-billingAddress=true
data-shippingAddres=true
data-amount="{{ amount }}"
data-name="StoreFront Name"
data-description="Custom-Made Jewellery"
data-image="../images/www/logo.png"
data-locale="auto">
</script>
</form>
Evrything up to this point is working fine. I submit the form and stripe returns the token but the form goes to the server following the route localhost/order (without the # symbol) instead of angular's localhost/#/order.
Why is stripe forcing this redirect? In other words why isn't angular capturing this return call?
Anyways. Then I create a route with Laravel to capture this and dump to inspect the returned data like so:
Route::post('/order', function($request){
dd($request);
});
Yep, data captured by stripe-generated form is returned except amount is missing... I mean everything including stripeToken, buyer's details such as: Name, Email, Billing and Shipping address are returned BUT detail regarding the amount is missing.
Is this normal or I'm I missing something?
Lastly currency is still showing the default: Where can I change currency from say USD to GBP?
Thanks in advance
1/ I don't think Checkout is forcing the redirect, but I don't know enough about Angular to explain what's going on, sorry.
2/ Yes, this is normal. The amount passed to Checkout in the data-amount configuration option is used for display purposes only. The actual amount that is charged is the one you pass in the amount parameter in the charge creation request in your server-side code.
If you need the amount to be user-specified (for instance, if you're taking donations), you'll need to add the amount to the form. Here is a simple JSFiddle to illustrate this case: https://jsfiddle.net/ywain/g2ufa8xr/
3/ You can use the data-currency parameter to change the currency displayed in the Checkout form. Just like data-amount, this is for display purposes only and the actual currency used for the charge is specified by the currency parameter in the charge creation.
This is what i managed to do.
I went with the custom form approach. I had a form template to capture both customer and card inputs in billing.template.html like so:
<form method="POST" id="payment-form">
<span class="payment-errors"></span>
<div>
<label>Name</label>
<input type="text" name="name" data-stripe="name">
</div>
<div>
<label>Email</label>
<input type="text" name="email" data-stripe="address_email">
</div>
<div>
<label>Address Line 1</label>
<input type="text" name="street" data-stripe="address_line1">
</div>
<div>
<label>Postcode</label>
<input type="text" name="postcode" data-stripe="address_zip">
</div>
<div>
<label for="country">Country</label>
<select ng-include="'../templates/_partials/_countrylist.html'"
id="countries" name="country" class="form-control"
name="country" ng-model="country" id="country" size="2"
data-stripe="address_country" required></select>
</div>
<div class="form-row">
<label>
<span>Card Number</span>
<input type="text" name="cardNumber" size="20" data-stripe="number"/>
</label>
</div>
<div class="form-row">
<label>
<span>CVC</span>
<input type="text" name="cvc" size="4" data-stripe="cvc"/>
</label>
</div>
<div class="form-row">
<label>
<span>Expiration (MM/YYYY)</span>
<input type="text" name="expMonth" size="2" data-stripe="exp-month"/>
</label>
<span> / </span>
<input type="text" name="expYear" size="4" data-stripe="exp-year"/>
</div>
<button id="customButton">Pay with Card</button>
</form>
I know we are not supposed to use name attribute in those form inputs but i left them so i could use angular validation, but i remove them using jquery before submitting to server.
Now i created a controller to handle the form: BillingController.js. In there i had an "on click" handler which kick started things by getting a hold of the form and doing some preparatory work: disabling button to prevent further clicks and removing those 'dreaded' name attributes, comme ca:
$('#customButton').on('click',function(event) {
var $form = $('#payment-form');
// Disable the submit button to prevent repeated clicks
$form.find('button').prop('disabled', true);
//NOW REMOVE THOSE NAME ATTRIBUTES
$form.find('input').removeAttr('name');
// call Stripe object and send form data to get back the token.
// NOTE first argument is $form
Stripe.card.createToken($form, stripeResponseHandler);
// Prevent the form from submitting with the default action
return false;
});
Now let me quote the documentation here as this is very important to understand: https://stripe.com/docs/tutorials/forms
The important code to notice is the call to Stripe.card.createToken.
The first argument is the form element containing credit card data
entered by the user. The relevant values are fetched from their
associated inputs using the data-stripe attribute specified in the
form.
Next we create stripeResponseHandler(). Remember it was the second argument in Stripe.card.createToken($form, stripeResponseHandler); above which gets called when Stripe returns the token.
function stripeResponseHandler(status, response) {
var $form = $('#payment-form');
if (response.error) {
// Show the errors on the form
$form.find('.payment-errors').text(response.error.message);
$form.find('button').prop('disabled', false);
} else {
// response contains id and card, which contains additional card details
var token = response.id;
// Insert the token into the form so it gets submitted to the server
$form.append($('<input type="hidden" name="stripeToken" />').val(token));
// and submit
$form.get(0).submit();
}
};
This is copy and paste stuff from stripe's own documentation: https://stripe.com/docs/tutorials/forms. Now, I want to say that, this is where a lot of us were tripping over the fact that form was performing a redirect etc. - notice final line $form.get(0).submit(); . Thats what caused the auto submit, redirecting to what ever action was on form, if u had any (in my case action attribute wasn't necessary as i was doing redirects in my controller).
So i decided to remove $form.get(0).submit() and implemented my own redirect after i was done sending data to the server.
NOTE: Stripe's response will have included data from the $form - try console.log(response); to have an idea of what's being posted back.
FINALLY:
We check if there were any errors returned and if so display them. Otherwise its all good, send data to the server.
The final code looks like:
function stripeResponseHandler(status, response) {
var $form = $('payment-form');
if (response.error) {
// Show the errors on the form
$form.find('.payment-errors').text(response.error.message);
} else {
// response contains id and card, which contains additional card details
var token = response.id;
// prepare data
var data = {
stripeToken: token,
fullName: response.card.name,
street: response.card.address_line1,
postcode: response.card.address_zip,
town: response.card.address_city,
country: response.card.address_country,
last4: response.card.last4
};
// send to server
$http.post('/checkout', data).then(function(result){
// here you can redirect yourself.
window.location.href = "/#/order-complete";
});
}
};
Angular really playing well with stripe here. Check out this link also: https://gist.github.com/boucher/1750368 - learn a lot from it.
I hope it helps someone today. Happy coding!
Stripe doesn't get involved with your form aside from preventing the default action on form submit event and stopping event propagation. Once the checkout process completes, it appends the relevant data to your form and then triggers a form submit event that is handled by HTML / Javascript natively.
I recommend using something like https://github.com/tobyn/angular-stripe-checkout to get your Stripe response handled correctly by Angular.
Otherwise you could add ng-submit="handleStripeCheckout($event)" to your form instead of action="/#/form". When Stripe's checkout process completes, your $scope.handleStripeCheckout method will be run and you can analyze the new form data inside that method.
Edit: Stripe checkout.js actually triggers form.submit(). That's a pretty bad bug on their part considering that almost no browsers handle that correctly. (Form submitted using submit() from a link cannot be caught by onsubmit handler)
I am very new to Orchard.
I have created a new theme, based on the Minty theme. The only real change is the layout, where I have adapted the html from an existing asp.net masterpage to match the orchard style razor layout.cshtml. I have experience with MVC and razor, so no problem on that side... unless I have missed something vital.
The problem is the login page. Clicking the sign in link takes me to the correct url without errors, but not login form gets rendered. I have checked that this is the case by Inspecting Element in google chrome.
I am aware that setting up widgets, etc, I can make content appear. However, I can't find how the login form gets inserted when the login url gets requested. I presume it uses the Orchard.Users module, but not sure how. Does it need a specific zone? I can't see why, but see how else.
As a result, I can't solve my problem...
Any pointers?
Any books or other learning media?
The code for my layout.cshtml is:
#functions {
// To support the layout classifaction below. Implementing as a razor function because we can, could otherwise be a Func<string[], string, string> in the code block following.
string CalcuClassify(string[] zoneNames, string classNamePrefix) {
var zoneCounter = 0;
var zoneNumsFilled = string.Join("", zoneNames.Select(zoneName => { ++zoneCounter; return Model[zoneName] != null ? zoneCounter.ToString() : "";}).ToArray());
return HasText(zoneNumsFilled) ? classNamePrefix + zoneNumsFilled : "";
}
}
#{
/* Global includes for the theme
***************************************************************/
SetMeta("X-UA-Compatible", "IE=edge,chrome=1");
Style.Include("http://fonts.googleapis.com/css?family=Handlee");
Style.Include("http://html5shiv.googlecode.com/svn/trunk/html5.js");
Style.Include("site.css");
Script.Require("jQuery").AtHead();
Script.Require("jQueryUI_Core").AtHead();
Script.Require("jQueryUI_Tabs").AtHead();
Script.Include("http://cdnjs.cloudflare.com/ajax/libs/modernizr/2.0.4/modernizr.min.js").AtHead();
Style.Include("TagDefaults.css");
Style.Include("LayoutStructure.css");
Style.Include("LayoutStyling.css");
Style.Include("TopMenu.css");
Style.Include("LeftBlock.css");
Style.Include("RightBlock.css");
Style.Include("MenuAdapter.css");
Style.Include("Content.css");
Style.Include("FloatedBoxes.css");
Style.Include("Helen.css");
/* Some useful shortcuts or settings
***************************************************************/
Func<dynamic, dynamic> Zone = x => Display(x); // Zone as an alias for Display to help make it obvious when we're displaying zones
/* Layout classification based on filled zones
***************************************************************/
//Add classes to the wrapper div to toggle aside widget zones on and off
var asideClass = CalcuClassify(new [] {"Sidebar"}, "aside-"); // for aside-1, aside-2 or aside-12 if any of the aside zones are filled
if (HasText(asideClass)) {
Model.Classes.Add(asideClass);
}
//Add classes to the wrapper div to toggle tripel widget zones on and off
var tripelClass = CalcuClassify(new [] {"TripelFirst", "TripelSecond", "TripelThird"}, "tripel-"); // for tripel-1, triple-2, etc. if any of the tripel zones are filled
if (HasText(tripelClass)) {
Model.Classes.Add(tripelClass);
}
//Add classes to the wrapper div to toggle quad widget zones on and off
var footerQuadClass = CalcuClassify(new [] {"FooterQuadFirst", "FooterQuadSecond", "FooterQuadThird", "FooterQuadFourth"}, "split-"); // for quad-1, quad-2, etc. if any of the quad zones are filled
if (HasText(footerQuadClass)) {
Model.Classes.Add(footerQuadClass);
}
var slideshowClass = CalcuClassify(new[] {"HomeSlideshow"}, "slideshow-");
if (HasText(slideshowClass)) {
Model.Classes.Add(slideshowClass);
}
/* Inserting some ad hoc shapes
***************************************************************/
//WorkContext.Layout.Header.Add(New.Branding(), "5"); // Site name and link to the home page
//WorkContext.Layout.Footer.Add(New.BadgeOfHonor(), "5"); // Powered by Orchard
WorkContext.Layout.Footer.Add(New.User(), "10"); // Login and dashboard links
/* Last bit of code to prep the layout wrapper
***************************************************************/
Model.Id = "layout-wrapper";
var tag = Tag(Model, "div"); // using Tag so the layout div gets the classes, id and other attributes added to the Model
}
#tag.StartElement
<a name="top"></a>
<div id="SiteHeader">
</div>
<div id="PageContainer">
<div style="position: absolute; Left:-80px; top:-88px;z-index:1000;">
<img id="bird" title="Pheasant" src="/Themes/TheFarmsBlogs/Styles/Images/PositionedImages/pheasant.gif" />
</div>
<div class="SiteMenu"><p>Hello Menu</p></div>
<div id="Specialized">
<div id="PageName">
<!--
PageName NOT in use!
-->
</div>
#if (Model.RightColumn != null) {
<div id="RightCol">
#Zone(Model.RightColumn)
</div>
}
<!-- Page divided into two main columns, of which the left column is subdivided as necessary -->
<div id="LeftCol">
<div id="PageBanner">
<div id="PageBannerLeft">
#if (Model.MainImage != null) {
<div id="PageBannerImage">
#Zone(Model.MainImage)
</div>
}
#if(Model.TheStrip != null) {
<div id="TheStrip">
#Zone(Model.TheStrip)
</div>
}
</div>
</div>
<div id="SpecializedContent">
#if(#Model.content != null)
{
#Zone(Model.content)
}
</div>
</div>
<div id="SpecializedFooter">
</div>
</div>
<div id="PageFooter">
#if (Model.FooterPage != null){
#Zone(Model.FooterPage)
}
</div>
</div>
<div id="SiteFooter">
#Display(Model.Footer)
The Farms Ltd - © 2007
</div>
#tag.EndElement
PS: the branding and badge of honour are commented out as I am only enabling bit by bit to eliminate the source of errors. It will be in the live site.
ADDENDUM:
See Bertrand Le Roy's answer below. The Orchard.Users module requires a Content zone with a Capital C. That instantly cured the problem.
I added this as Bertrand's response was tentative, and I wanted to reinforce that the problem was the name of the zone.
In Orchard.Users, look for Controllers/AccountController.cs. In there, there is a LogOn action. It creates a LogOn shape that it then puts in a shape result. This then gets resolved as the Views/LogOn.cshtml template (which you can override in your theme by just dropping a file with the same name in there, for example a copy of the original that you can tweak). The LogOn template will be rendered within the theme's layout, in the Content zone. Does this answer your question?
I think the mistake you made was to name your Content zone content (notice the casing).
I am using a customised version of search-theme-form.tpl
When I use the search box, I do get transferred to the search page. But the search does not actually take place. The search box on the search results page does work though. This is my search-them-form.tpl.php file (demo :
<input type="text" name="search_theme_form_keys" id="edit-search-theme-form-keys" value="Search" title="Enter the terms you wish to search for" class="logininput" height="24px" onblur="restoreSearch(this)" onfocus="clearInput(this)" />
<input type="submit" name="op" id="edit-submit" value="" class="form-submit" style="display: none;" />
<input type="hidden" name="form_token" id="edit-search-theme-form-form-token" value="<?php print drupal_get_token('search_theme_form'); ?>" />
<input type="hidden" name="form_id" id="edit-search-theme-form" value="search_theme_form" />
There is also a javascript file involved. I guess it's use is pretty clear from the code:
function trim(str) {
return str.replace(/^\s+|\s+$/g, '');
}
function clearInput(e) {
e.value=""; // clear default text when clicked
e.className="longininput_onfocus"; //change class
}
function restoreSearch(e) {
if (trim(e.value) == '') {
{
e.value="Search"; // reset default text onBlur
e.className="logininput"; //reset class
}
}
}
What can be the problem and how can I fix it?
Apparently, you cannot directly modify the HTML in search-theme-form.tpl.php since thats not the right way to do it. So my adding the class and onFocus and onBlur attributes was the problem.
The correct way to do it is to modify the themes template.php file. Basically we will be using form_alter() to modify the form elements. Since using the HTML way is wrong. Take a look at the code below (taken from : here )
<?php
/**
* Override or insert PHPTemplate variables into the search_theme_form template.
*
* #param $vars
* A sequential array of variables to pass to the theme template.
* #param $hook
* The name of the theme function being called (not used in this case.)
*/
function yourthemename_preprocess_search_theme_form(&$vars, $hook) {
// Note that in order to theme a search block you should rename this function
// to yourthemename_preprocess_search_block_form and use
// 'search_block_form' instead of 'search_theme_form' in the customizations
// bellow.
// Modify elements of the search form
$vars['form']['search_theme_form']['#title'] = t('');
// Set a default value for the search box
$vars['form']['search_theme_form']['#value'] = t('Search this Site');
// Add a custom class and placeholder text to the search box
$vars['form']['search_theme_form']['#attributes'] = array('class' => 'NormalTextBox txtSearch',
'onfocus' => "if (this.value == 'Search this Site') {this.value = '';}",
'onblur' => "if (this.value == '') {this.value = 'Search this Site';}");
// Change the text on the submit button
//$vars['form']['submit']['#value'] = t('Go');
// Rebuild the rendered version (search form only, rest remains unchanged)
unset($vars['form']['search_theme_form']['#printed']);
$vars['search']['search_theme_form'] = drupal_render($vars['form']['search_theme_form']);
$vars['form']['submit']['#type'] = 'image_button';
$vars['form']['submit']['#src'] = path_to_theme() . '/images/search.jpg';
// Rebuild the rendered version (submit button, rest remains unchanged)
unset($vars['form']['submit']['#printed']);
$vars['search']['submit'] = drupal_render($vars['form']['submit']);
// Collect all form elements to make it easier to print the whole form.
$vars['search_form'] = implode($vars['search']);
}
?>
In yourthemename_preprocess_search_theme_form - 'yourthemename' will obviously reflect the name of your custom theme. Basically the code is self-explanatory. what with the comments and all.
So, basically thats the way it works.