I am new to coffee scripting..This is my first script and is not working..
html
head title='hello world'
css:
h1{
color: blue;
}
coffee:
number = 42
opposite = true
saybye = () ->
alert 'hello'+number
''
body
h1
|hello world
input type="button" onclick="saybye()" value="sayhello"
scss:
$blue: #3bbfce;
h2{
color: $blue;
}
It says saybye is not found and should I always end with some expression which will get return. Is there a way to stop the return call?
I don't know what Slim does but CoffeeScript usually gets wrapped in a function to avoid namespace pollution. So, your CoffeeScript probably ends up being converted to JavaScript something like this:
(function() {
var number, opposite, saybye;
number = 42;
opposite = true;
saybye = function() {
alert('hello' + number);
return '';
};
})();
The result is that saybye is not visible to your HTML.
You really shouldn't be using onclick in 2012, you should be binding to the event through the modern API. If you're using jQuery, you'd do it like this:
coffee:
number = 42
opposite = true
saybye = () ->
alert 'hello'+number
''
$ -> $('input[type=button]').click saybye
If you're not using jQuery (or similar), then you'd do it the hard way using addEventListener on the raw DOM object. Alternatively, you could put them in window yourself and bypass scope protection:
coffee:
window.number = 42
window.opposite = true
window.saybye = () ->
alert 'hello' + number
As far as returning something goes, don't worry about it, return whatever makes sense. Sometimes returning undefined makes sense, sometimes it doesn't. If there is no obvious return value then just let CoffeeScript do what it wants, go with this:
saybye = () ->
alert 'hello' + number
and move on to more interesting problems.
Related
I have a question regarding how protractor handles the locating of elements.
I am using page-objects just like I did in Webdriver.
The big difference with Webdriver is that locating the element only happens when a function is called on that element.
When using page-objects, it is advised to instantiate your objects before your tests. But then I was wondering, if you instantiate your object and the page changes, what happens to the state of the elements?
I shall demonstrate with an example
it('Change service', function() {
servicePage.clickChangeService();
serviceForm.selectService(1);
serviceForm.save();
expect(servicePage.getService()).toMatch('\bNo service\b');
});
When debugging servicePage.getService() returns undefined.
Is this because serviceForm is another page and the state of servicePage has been changed?
This is my pageobject:
var servicePage = function() {
this.changeServiceLink = element(by.id('serviceLink'));
this.service = element(by.id('service'));
this.clickChangeService = function() {
this.changeServiceLink.click();
};
this.getService = function() {
return this.service.getAttribute('value');
};
};
module.exports = servicePage;
Thank you in advance.
Regards
Essentially, element() is an 'elementFinder' which doesn't do any work unless you call some action like getAttribute().
So you can think of element(by.id('service')) as a placeholder.
When you want to actually find the element and do some action, then you combine it like element(by.id('service')).getAttribute('value'), but this in itself isn't the value that you are looking for, it's a promise to get the value. You can read all about how to deal with promises elsewhere.
The other thing that protractor does specifically is to patch in a waitForAngular() when it applies an action so that it will wait for any outstanding http calls and timeouts before actually going out to find the element and apply the action. So when you call .getAttribute() it really looks like
return browser.waitForAngular().then(function() {
return element(by.id('service')).getAttribute('value');
});
So, in your example, if your angular pages aren't set up correctly or depending on the controls you are using, you might be trying to get the value before the page has settled with the new value in the element.
To debug your example you should be doing something like
it('Change service', function() {
servicePage.getService().then(function(originalService) {
console.log('originalService: ' + originalService);
});
servicePage.clickChangeService();
serviceForm.selectService(1);
serviceForm.save();
servicePage.getService().then(function(newService) {
console.log('newService: ' + newService);
});
expect(servicePage.getService()).toMatch('\bNo service\b');
});
The other thing that I'm seeing is that your pageObject appears to be a constructor when you could just use an object instead:
// name this file servicePage.js, and use as 'var servicePage = require('./servicePage.js');'
module.exports = {
changeServiceLink: element(by.id('serviceLink')),
service: element(by.id('service')),
clickChangeService: function() {
this.changeServiceLink.click();
},
getService: function() {
return this.service.getAttribute('value');
}
};
Otherwise you would have to do something like module.exports = new servicePage(); or instantiate it in your test file.
When you navigate another page, the web elements will be clear, that you selected. So you have to select again. You can select all elements that is in a page of HTML. You can click that you see. So the protactor + Selenium can decide what is displayed.
You have a mistake in your code, try this:
expect(servicePage.getService()).toMatch('\bNo service\b');
When using iron-router to scroll to a hash using an anchor button referencing the id of the next section, such as this:
<a class="button" href="{{pathFor 'home' hash='about'}}">
iron-router happily takes us to the about section the first time the button is clicked.
if you scroll back up using the mouse and click the same button a second time, no scrolling takes place.
I presume this is because the destination is apparently the same as the current router location, hence no reaction is triggered.
How can I force a reaction?
I've tried clearing the hash in the window.location in an override to the scrollToHash function:
Router._scrollToHash = function(hash) {
var section = $(hash);
if (section.length) {
var sectionTop = section.offset().top;
$("html, body").animate({
scrollTop: sectionTop
}, "slow");
}
window.location.hash = '';
};
And this allows a second click but no more, which has me puzzled.
Setting window.location.hash to a space rather than an empty string works:
Router._scrollToHash = function(hash) {
var section = $(hash);
if (section.length) {
var sectionTop = section.offset().top;
$("html, body").animate({
scrollTop: sectionTop
}, "slow");
}
window.location.hash = ' ';
};
Probably setting it to a non-existent hash would also.
In addition to cover cases where there is a menu link that can be activated from a second page to take you to the hash you need this package:
meteor add okgrow:iron-router-autoscroll
Used in combination it covers every case I have thought of.
We're aware of that amazing trick which allows users to highlight a link. But, you must repeat it for each link. for example: a href="https://www.yahoo.com" Onclick="window.open(this.href); return false" onmouseout="this.style.color = '#0000ff';" onmouseover="this.style.color = '#e3FF85';" align="justify">Yahoo. But, I would like this code to apply to every link on the page. I've explored 2 possible methods. One is to use STYLE TYPE and CLASS= methods. Another possibility is using STYLE H1 /H1 (similar to W3 schools). But, I haven't even come close to getting a universal application.
1. You can try this:
var links = document.getElementsByTagName('a');
for (var i = 0; i < links.length; ++i)
{
links[i].onmouseenter = function() {links[i].style.color = '#e3FF85';};
links[i].onmouseout= function() {links[i]..style.color = '#0000ff';};
}
You get the list of all links using getElementsByTagName('a') ('a' is tag name for links), and you can do anything you want with them.
2. You can also try it with jquery:
var allLinks = $('a');
allLinks.mouseenter(function() { $(this).css('color', '#e3FF85'); });
allLinks.mouseout(function() { $(this).css('color', '#0000ff'); })
3. If you just care about changing style (like color or background) when mouse is over your link, you can do it from CSS:
a:hover
{
color: #123456;
}
I'm using the jQuery ajax form plugin in my WordPress plugin's settings page. Before I started using ajax, I had this script that compared text input values to placeholder values, and if they matched, set the text input value to null. But it no longer works now that I'm using ajax. With the jQuery ajax form plugin, I can pass arguments in a beforeSerialize function, or in a beforeSubmit function. I think it would need to be done in the beforeSerialize. Anyway, I'm not sure how to make this work. Here is the script that was working before I switched to ajax:
$('[placeholder]').focus(function() {
var input = $(this);
if (input.val() == input.attr('placeholder')) {
input.val('');
input.removeClass('placeholder');
}
}).blur(function() {
var input = $(this);
if (input.val() == '' || input.val() == input.attr('placeholder')) {
input.addClass('ssfa-placeholder');
input.val(input.attr('placeholder'));
}
}).blur().parents('form').submit(function() {
$(this).find('[placeholder]').each(function() {
var input = $(this);
if (input.val() == input.attr('placeholder')) {
input.val('');
}
})
});
And here is my current script for the ajax form submit:
var svn = $("#ssfa-saving"),
bck = $("#ssfa-saving-backdrop"),
svd = $("#ssfa-settings-saved");
$("#ssfa-form").ajaxForm({
beforeSend: function() {
svn.fadeIn('slow');
bck.fadeIn('fast');
},
success: function(){
svn.fadeOut('slow');
svd.delay(1000).fadeIn('slow').delay( 2500 ).fadeOut('slow');
bck.delay( 4500 ).fadeOut('slow');
}
});
Any ideas on how I can get the ajax submit (either beforeSerialize or beforeSend ) to ignore placeholder values? This first script above was a really simple solution for regular post submit. I'm hoping I can find something just as simple for ajax.
UPDATE
I worked out a basic way of doing it but it involves calling each text field that has a placeholder, so it's not exactly elegant like the original script, but this is functional:
$("#ssfa-form").ajaxForm({
beforeSerialize: function() {
var permex = $('input#exclusions');
$('input[id^=bs]').each(function(){
var bs = $(this);
if (bs.val() === 'Display Name')
bs.removeAttr('value');
});
$('input[id^=custom_]').each(function(){
var cs = $(this);
if (cs.val() === 'classname1|Display Name 1, classname2|Display Name 2')
cs.removeAttr('value');
});
if (permex.val() === '.avi, My Embarrasing Photograph, .tif')
permex.removeAttr('value');
},
beforeSend: function() {
etc.
And since it's a placeholder text, the text doesn't actually disappear when the value attribute is removed, so no one is really the wiser. I'm not over the moon with this, but it works. If I had a much larger form, this wouldn't be workable.
Open to better ideas....
Well, I played around with it quite a bit more and found a way to get the original code to work with ajax submit. It's quite simple actually. I just had to specify the element within which to search for the placeholder attr. Here it is:
beforeSerialize: function() {
$("#ssfa-form").find('[placeholder]').each(function() {
var input = $(this);
if (input.val() == input.attr('placeholder')) {
input.val('');
}
})
},
etc.
To track the issue, see:
https://github.com/mathiasbynens/jquery-placeholder/issues/30
https://github.com/mathiasbynens/jquery-placeholder/issues/197
This link (archived version) describes how to inject code from a script into an iframe:
function injectJS() {
var iFrameHead = window.frames["myiframe"].document.getElementsByTagName("head")[0];
var myscript = document.createElement('script');
myscript.type = 'text/javascript';
myscript.src = 'myscript.js'; // replace this with your SCRIPT
iFrameHead.appendChild(myscript);
}
That's ok, but what if I want to insert a function object into an iframe and get it executed in the iframe context? Let's say I have:
function foo () {
console.log ("Look at me, executed inside an iframe!", window);
}
and I want to insert foo's code inside an iframe? (function foo could be something loaded dynamically, I can't just wrap it in quotes)
I naively tried:
var scriptFooString = "<script>" + foo.toString() + "</script>"
to get the code inside function, but
I don't know how to insert it in the iframe HEAD (maybe with jquery?)
I don't know if it's the right way
I don't know what happens when if function is way more complex than that
I don't know what happens with double and single quotes in scriptFooString
Any hint?
First of all you can only accomplish this if your frame and the page displaying it is within the same domain (Due to cross-domain rules)
secondly you can manipulate dom and window objects of the frame directly through JS:
frames[0].window.foo = function(){
console.log ("Look at me, executed inside an iframe!", window);
}
to get your frame from a DOMElement object you can use:
var myFrame = document.getElementById('myFrame');
myFrame.contentWindow.foo = function(){
console.log ("Look at me, executed inside an iframe!");
}
Note that the scope in foo is NOT changed, so window is still the parent window etc. inside foo.
If you want to inject some code that needs to be run in the context of the other frame you could inject a script tag, or eval it:
frames[0].window.eval('function foo(){ console.log("Im in a frame",window); }');
Though the general consensus is to never use eval, I think its a better alternative than DOM injection if you REALLY need to accomplish this.
So in your specific case you could do something like:
frames[0].window.eval(foo.toString());
This code is the result of my research. The accepted answer also helped me a lot.
First of all, I create a simple iframe:
<iframe id="myiframe" width="200" height="200" srcdoc="<h1 id='title'>Hello from Iframe</h1><button type='button' id='fire'>Click Me!</button>
"></iframe>
For access to iframe's window and document I used this code.
const iframe = document.getElementById('myiframe');
const iframeWin = iframe.contentWindow || iframe;
const iframeDoc = iframe.contentDocument || iframeWin.document;
Finally I injected js codes into iframe:
var script = iframeDoc.createElement("script");
script.append(`
window.onload = function() {
document.getElementById("fire").addEventListener('click', function() {
const text = document.getElementById('title').innerText;
alert(text);
})
}
`);
iframeDoc.documentElement.appendChild(script);
Demo:
https://jsfiddle.net/aliam/1z8f7awk/2/
Here's my solution. I'm using jquery to insert the content, then using eval to execute the script tags in the context of that iframe:
var content = $($.parseHTML(source, document, true));
$("#content").contents().find("html").html(content);
var cw = document.getElementById("content").contentWindow;
[].forEach.call(cw.document.querySelectorAll("script"), function (el, idx) {
cw.eval(el.textContent);
});