I'm trying to make a chrome extension that fills out a form in a specific page. (You can think of it like LastPass filling the user and passwords)
Currently I'm trying to fill out a form for a credit card with Javascript, but the website just gets stucked loading when you submit the form if you insert the credit card or other information through Javascript.
I have currently tried adding the card with this document.querySelector("#cardNumber").value="5454545454545454"; and I have tried doing .focus() or .click() on that input, even adding the value digit by digit, but it doesn't seem to trigger the website events that save the credit card correctly.
So, what I'm currently trying to figure out is if there's any way I can simulate the user adding the form so the website events get triggered. (The website is Mercadolibre.com, but I don't know if that helps in any way).
Here's a snippet of the website's code that gets executed when the card number changes.
App.module("Checkout.Payments.Views", function(e, t, n, i, o) {
var s = {
cardNumber: "number",
ownerName: "name",
expirationDate: "expiration",
securityCode: "security",
brand: "brand"
};
e.CardContainer = i.ItemView.extend({
getTemplate: function() {
return t.Utils.getTemplate(t.Components.templates.card, '[data-js="card"]')
},
className: "new-card__container--view",
ui: {
card: ".ui-card"
},
initialize: function() {
var e = this;
this.on("refresh", this.onCardRefresh),
this.on("rotate", this.onRotate),
this.on("brandSet", this.onBrandSet),
this.on("showSecurityHint", this.onShowSecurityHint),
this.modelEvents = {},
this.model.keys().forEach(function(t) {
"binHelper" !== t && (e.modelEvents["change:" + t] = function() {
return e.onCardDataChanged({
key: t,
rotate: "securityCode" === t
})
})
}),
this.bindEntityEvents(this.model, this.modelEvents),
this.brandSet = !1
},
onBrandSet: function() {
this.brandSet = !0
},
onShow: function() {
this.cardComponent = new CardComponent,
o(this.cardComponent.cardElements.name).attr("data-title", this.model.get("ownerNamePlaceholder")),
this.trigger("refresh")
},
onCardRefresh: function() {
var e = this;
this.model.keys().forEach(function(t) {
e.model.trigger("change:" + t, {
rotate: !1
})
})
},
onCardDataChanged: function(e) {
var t = e.key + "Changed",
n = t.replace(/\w{1}/, function(e) {
return e.toUpperCase()
}),
i = "on" + n;
this.triggerMethod(this[i] instanceof Function ? t : "otherCardDataChanged", e.key)
},
onCardNumberChanged: function(e) {
var t = this.model.get(e);
this.cardComponent[s[e]] = this.model.get(e),
t.length < 6 && (this.model.set("brand", ""),
this.model.set("bin", ""),
this.brandSet = !1)
},
onCardOwnerNameChanged: function(e) {
var t = this.model.get(e) || this.model.get("ownerNamePlaceholder");
this.cardComponent[s[e]] = t
},
onOtherCardDataChanged: function(e) {
this.cardComponent[s[e]] = this.model.get(e),
"" !== this.model.get("brand") && this.trigger("brandSet")
},
onRotate: function(e) {
"front" === e ? (this.cardComponent.rotateFront(),
this.cardComponent.blur = "securityFront") : "back" === e && this.cardComponent.rotateBack()
},
onShowSecurityHint: function() {
this.cardComponent.focus = "securityFront"
}
})
}),
The solution for this issue is sending events that get triggered when the user is typing manually. For example the events (blur, change, and input).
I added the events as constants like so:
const EVENT_OPTIONS = {bubbles: true, cancelable: false, composed: true};
const EVENTS = {
BLUR: new Event("blur", EVENT_OPTIONS),
CHANGE: new Event("change", EVENT_OPTIONS),
INPUT: new Event("input", EVENT_OPTIONS),
};
Afterwards, depending on the website, you might only need to add some of them or all like this:
const inputElement = document.querySelector("#cardNumber");
inputElement.value = "5454545454545454";
inputElement.dispatchEvent(EVENTS.INPUT);
In other websites, you can simulate the events better sending the CHANGE event and then the BLUR. And for other websites that use React, you might need to use React's _valueTracker like so:
const inputElement = document.querySelector("#cardNumber");
inputElement.value = "5454545454545454";
const tracker = inputElement._valueTracker;
tracker && tracker.setValue("5454545454545454");
inputElement.dispatchEvent(EVENTS.INPUT);
inputElement.dispatchEvent(EVENTS.BLUR);
Related
I asked this question by gitHub, but I try it here as well maybe someone knows the issue.
I am using slim select plugin, when I try to select the second option after searching I get an error
see picture:
But when I click outside so that the drop down goes closed and start searching again then I can select many options without getting the error.
Here is the code snippet where slimselect is initialized:
function initDropDowns () {
$('[slimselect]').each(function (index) {
var id = $(this).attr("id");
var placeholder = $(this).data("placeholder");
var selectByGroup = true;
var compareDropDown = false;
var dataSelectByGroup = $(this).data("selectbygroup");
var dataCmpareDropdown = $(this).data("comparedropdown");
if (dataSelectByGroup) {
selectByGroup = dataSelectByGroup === "true";
}
if (dataCmpareDropdown) {
compareDropDown = dataCmpareDropdown === "true";
}
var that = this;
var select = new SlimSelect({
select: '#' + id,
placeholder: placeholder,
showSearch: true,
searchText: settings.texts.noSearchResults,
searchPlaceholder: settings.texts.searchPlaceholder,
searchingText: settings.texts.searchingText,
searchHighlight: true,
closeOnSelect: false,
showOptionTooltips: true,
selectByGroup: selectByGroup,
hideSelectedOption:true,
limit: 5,
beforeOnChange: function (info) {
if ($(that).data('singleselect') === true) {
dropDowns[that.id].set ([]);
}
},
onChange: slimSelectOnChange
});
dropDowns[id] = select;
});
}
I have a model say 'my.attendance' , also have a form view for this which contains some attendance details.What i need is when i open this form view it should always open in Edit mode.So i can directly enter the attendance without clicking Edit button each time.
You have to extend the ViewManager to achieve this.
odoo.define('my_module.view_manager', function (require) {
"use strict";
var ViewManager = require('web.ViewManager');
ViewManager.include({
custom_events: {
execute_action: function(event) {
var data = event.data;
this.do_execute_action(data.action_data, data.env, data.on_closed)
.then(data.on_success, data.on_fail);
},
search: function(event) {
var d = event.data;
_.extend(this.env, this._process_search_data(d.domains, d.contexts, d.groupbys));
this.active_view.controller.reload(_.extend({offset: 0}, this.env));
},
switch_view: function(event) {
if ('res_id' in event.data) {
this.env.currentId = event.data.res_id;
}
var options = {};
console.log(event.data)
if (event.data.view_type === 'form' && !this.env.currentId) {
options.mode = 'edit';
} else if (event.data.mode) {
options.mode = event.data.mode;
}
// Extra added code
if (event.data.model){
if (event.data.model == 'my.model'){ // Checking the particular model.
options.mode = 'edit';
}
}
this.switch_mode(event.data.view_type, options);
},
env_updated: function(event) {
_.extend(this.env, event.data);
},
push_state: function(event) {
this.do_push_state(event.data);
},
get_controller_context: '_onGetControllerContext',
switch_to_previous_view: '_onSwitchToPreviousView',
},
});
});
currently i'm starting with Ember, and i'm loving it! I'm with some difficulties, especially when it comes to components.
For you to understand, I'm going through old code to Ember, and I would like to turn this code into a Component, but I do not know actually how to start, since I do not know how to catch the button being clicked, and I also realized that Ember has several helpers, maybe I do not need any of this giant code to do what I want.
This is the old code result: http://codepen.io/anon/pen/WQjobV?editors=110
var eventObj = {};
var eventInstances = {};
var actual;
var others;
var clicked;
var createEventInstance = function (obj) {
for (var key in obj) {
eventInstances[key] = new Event(obj[key]);
}
};
var returnStyle = function (inCommon) {
var $inCommon = inCommon;
$inCommon.css({
width: '342.4px',
minWidth: '342.4px'
});
$inCommon.find('.cta').removeClass('hidden');
$inCommon.find('.event-close').removeClass('inline');
$inCommon.find('.event-info_list').removeClass('inline');
$inCommon.removeClass('hidden');
$inCommon.find('.expanded').slideUp();
$inCommon.find('.expanded').slideUp();
$inCommon.find('.event-arrow').remove();
$inCommon.find('h2').find('ul').remove('ul');
};
var Event = function (id) {
this.id = id;
};
Event.prototype.expandForm = function () {
actual.css('width', '100%');
actual.find('.event-info_list').addClass('inline');
actual.find('.expanded').slideDown().css('display', 'block');
actual.find('.event-close').addClass('inline');
};
Event.prototype.close = function () {
returnStyle(actual);
returnStyle(others);
};
Event.prototype.hideElements = function () {
clicked.addClass('hidden');
others.addClass('hidden');
};
Event.prototype.maskPhone = function () {
$('[name$=phone]').mask('(99) 99999-9999', {
placeholder: '(00) 0000-0000'
});
};
$('.submit-form').on('click', function (e) {
e.preventDefault();
var id = '.' + $(this).data('id');
var name = $(id).children('#person-name').val();
var email = $(id).children('#person-email').val();
var guests = $(id).children('#person-obs.guests').val();
var phone = $(id).children('#person-phone').val();
var participants = $(id).children('#booking-participants').val();
if (name === '' || email === '' || phone === '' || participants === '' || guests === '') {
alert('Preencha os campos obrigatórios.');
} else {
$(id).submit();
}
});
Event.prototype.createDropDown = function () {
actual.find('h2').addClass('event-change')
.append('<span class="event-arrow" aria-hidden="true">â–¼</span>')
.append(function () {
var self = $(this);
var list = '<ul class="dropdown hidden">';
$('.event').each(function (index) {
if ($(this).find('h2')[0] != self[0]) {
list += '<li data-index="' + index + '">' + $(this).find('h2').text() + '</li>';
}
});
return list;
}).click(function () {
if ($(this).attr('data-expanded') == true) {
$(this).find('ul').toggleClass('hidden');
$(this).attr('data-expanded', false);
} else {
$(this).find('ul').toggleClass('hidden');
$(this).attr('data-expanded', true);
}
}).find('li').click(function (e) {
e.stopPropagation();
actual.find('.event-info_list').removeClass('inline');
actual.find('h2').attr('data-expanded', false);
actual.find('h2').removeClass('event-change');
actual.find('.expanded').slideUp().css('display', 'inline-block');
others.removeClass('hidden');
actual.find('.cta').removeClass('hidden');
actual.find('h2').find('.event-arrow').remove();
actual.find('h2').off('click');
actual.find('h2').find('ul').remove('ul');
$($('.event')[$(this).attr('data-index')]).find('.cta').trigger('click');
});
};
Event.prototype.open = function () {
actual = $('[data-id="' + this.id + '"]');
others = $('.event').not(actual);
clicked = actual.find('.cta');
this.hideElements();
this.expandForm();
this.createDropDown();
this.maskPhone();
};
$('.event').each(function (i, event) {
var prop = 'id' + $(event).data('id');
var value = $(event).data('id');
eventObj[prop] = value;
});
createEventInstance(eventObj);
Basically i have this boxes, which box represent one booking in some event (will be populate by the server). When the user clicks in one box, this boxes expands and the other disappear. But than a dropbox will be created with the other boxes, so the user can navigate in the events by this dropdown.
I didn't do much with Ember, i transform the "events" div into a component with the name "BookingBoxComponent" and two actions:
SiteApp.BookingBoxComponent = Ember.Component.extend({
actions:
open: function() {
// HOW COULD I ACCESS THE CLICKED BUTTON HERE?
},
close: function() {
}
});
As you can see, i put two actions, one for opening the box and other for closing, should i just put the logic in both, or i can improve this like a Ember way?
I don't know if i am asking to much here, so if i am, at least i would like to know how to access the button clicked in the open method, i was trying passing as a parameter, like:
<button {{action 'open' this}}></button>
But didn't work.
I could offer 50 of my points to someone who help transform the old cold in a Ember way code.
Thanks.
The event object will be passed with every action as the last parameter, so when you specified this you were actually passing whatever object has context in that block. In your open function, do not pass this and do
open: function(event) {
// event.currentTarget would be the button
}
And now you can do something like event.currentTarget or event.target
I am currently developing a calendar where activities can be drag&dropped to other days.
When an activity is dropped into a different day, I show a custom modal using durandal's dialog plugin. The problem is when an user closes the modal, the activity has to revert to its original position. When an activity is dropped the following code is called:
function showDroppedActivityModal(obj) {
require(['modals/droppedActivityModal/droppedActivityModal', 'moment'], function(droppedActivityModal, moment) {
droppedActivityModal.show(obj).then(function(response) {
if(response !== false) {
...
}
// dialog closes
else {
calendarView.revertActivity.notify({ revert: true})
}
});
});
}
In my calendarView I implemented the revertActivity event to set revert to true but the function never re-evaluates itself but i'm able to receive the new revert value (true).
$(activity).draggable({
revert: function() {
var revert = false;
self.revertActivity.attach(function(sender, args) {
revert = args.revert;
});
return revert;
}
});
Custom event code:
function CalendarEvent(sender) {
this._sender = sender;
this._listeners = [];
}
CalendarEvent.prototype = {
attach : function (listener) {
this._listeners.push(listener);
},
notify : function (args) {
var index;
for (index = 0; index < this._listeners.length; index += 1) {
this._listeners[index](this._sender, args);
}
},
remove : function (listener){
this._listeners.remove(listener);
}
};
this.revertActivity = new CalendarEvent(this);
may be you can help me:
Please check this demo: Plugin demo
Plugin description: Plugin description
How can I show only matching items at the top of the list and hide not matching during input typing?
Thanks!
You can do it using onShow and onHide handlers, of course.
See here: http://jsfiddle.net/ypfPY/3/
var myFilter = new ElementFilter('search-term', '#my-list li', {
trigger: 'keyup',
cache: false,
onShow: function(element) {
element.setStyle('display', 'block');
},
onHide: function(element) {
element.setStyle('display', 'none');
}
});
Frankly speaking, I encountered a set of bugs in ElementFilter source (bug on firstly retrieving "showing" and false instead of true for matchOverride when showing all items), so, you better get source from here:
var ElementFilter = new Class({
// implements
Implements: [Options,Events],
// options
options: {
cache: true,
caseSensitive: false,
ignoreKeys: [13, 27, 32, 37, 38, 39, 40],
matchAnywhere: true,
property: 'text',
trigger: 'mouseup',
onStart: $empty,
onShow: $empty,
onHide: $empty,
onComplete: $empty
},
//initialization
initialize: function(observeElement,elements,options) {
//set options
this.setOptions(options);
//set elements and element
this.observeElement = document.id(observeElement);
this.elements = $$(elements);
this.matches = this.elements;
this.misses = [];
//start the listener
this.listen();
},
//adds a listener to the element (if there's a value and if the event code shouldn't be ignored)
listen: function() {
//add the requested event
this.observeElement.addEvent(this.options.trigger,function(e) {
//if there's a value in the box...
if(this.observeElement.value.length) {
//if the key should not be ignored...
if(!this.options.ignoreKeys.contains(e.code)) {
this.fireEvent('start');
this.findMatches(this.options.cache ? this.matches : this.elements);
this.fireEvent('complete');
}
}
else{
//show all of the elements - changed to true
this.findMatches(this.elements,true);
}
}.bind(this));
},
//check for matches within specified elements
findMatches: function(elements,matchOverride) {
//settings
var value = this.observeElement.value;
var regExpPattern = this.options.matchAnywhere ? value : '^' + value;
var regExpAttrs = this.options.caseSensitive ? '' : 'i';
var filter = new RegExp(regExpPattern, regExpAttrs);
var matches = [];
//recurse
elements.each(function(el){
var match = matchOverride == undefined ? filter.test(el.get(this.options.property)) : matchOverride;
//if this element matches, store it...
if(match) {
// default value added
if(!el.retrieve('showing', true)){
this.fireEvent('show',[el]);
}
matches.push(el);
el.store('showing',true);
}
else {
if(el.retrieve('showing', true)) {
this.fireEvent('hide',[el]);
}
el.store('showing', false);
}
return true;
}.bind(this));
return matches;
}
});