Do you find yourself slow at reading?
If the answer is yes, well, you’re definitely not alone!
Continue reading “Harder Better Faster Stronger!…most of all Faster!!! Jetzt!”
Mikamai developers blog
Do you find yourself slow at reading?
If the answer is yes, well, you’re definitely not alone!
Continue reading “Harder Better Faster Stronger!…most of all Faster!!! Jetzt!”
Question: can I call JS functions from Opal?
Answer: totally!
Opal standard lib (stdlib) includes a native
module, let’s see how it works and wrap window
:
require 'native'
window = Native(`window`) # equivalent to Native::Object.new(`window`)
Now what if we want to access one of its properties?
window[:location][:href] # => "http://dev.mikamai.com/"
window[:location][:href] = "http://mikamai.com/" # will bring you to mikamai.com
And what about methods?
window.alert('hey there!')
So let’s do something more interesting:
class << window
# A cross-browser window close method (works in IE!)
def close!
%x{
return (#@native.open('', '_self', '') && #@native.close()) ||
(#@native.opener = null && #@native.close()) ||
(#@native.opener = '' && #@native.close());
}
end
# let's assign href directly
def href= url
self[:location][:href] = url
end
end
That’s all for now, bye!
window.close!
Working on a project for an important client (more on that in the next months), we came up with the need to inject jQuery on a page, making it available to your functions.
In addition to that we wanted to be sure it didn’t conflict with other jQueries the page was already using, and, if a new version of jQuery was already available, use that instead of injecting a new one.
This whole injection thing is not very complicated per se. You just have to be sure you’re not messing up with what’s already in the page.
Let’s walk through our solution to see how we implemented it. All the code you see here will be CoffeeScript, the javascript translation is really straightforward and won’t be provided 🙂
We start with an empty html document, something like this:
<!DOCTYPE html> <head> <meta charset="utf-8" /> <script src="jquery-injection.js"></script> </head> <body> </body>
jQuery-injection.js is the compiled version of our coffeescript source.
The simplest way to inject jQuery would be something like this:
@Injector = injectjQuery: -> script = document.createElement('script') script.src = "//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js" head = document.getElementsByTagName('head')[0] head.appendChild(script)
We’re creating a globally available Injector object with an injectjQuery function that creates a script element, sets its src attribute and adds it to head.
This is all fine and dandy, but what if we already have jQuery? This would load jQuery twice and there would be namespace conflicts. What we want to do is check for an existing jQuery and use jQuery.noconflict() to make sure they don’t clash.
checkAndInjectjQuery: -> if jQuery? if @versionCompare(jQuery.fn.jquery, "2.0.3") < 0 @injectjQuery(true) else @jq = window.jQuery else @injectjQuery(false)
What’s going on in here? We check if jQuery exists, and that its version is at least 2.0.3. The version check is done via versionCompare, a coffeescript simplified port of this code I found on StackOverflow.
If jQuery exists and is sufficiently new, we just map this.jq to window.jQuery. Our local jq variable is what we’re gonna use inside our functions instead of using $.
If you paid enough attention, you will have noticed we’re calling injectjQuery with a parameter.
injectjQuery: (saveOriginal) -> script = document.createElement('script') script.src = "http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js" head = document.getElementsByTagName('head')[0] if saveOriginal @jq = $.noConflict(true) head.appendChild(script)
injectjQuery(true) will assume that there’s another jQuery in the page, so it saves it (temporarily) in this.jq, and then cleans after it, making sure it won’t be there when the new jQuery loads.
Wait, did I say “when the new jquery loads”? You can bet I did! Everything we’re doing is asyncronous, so we can’t be sure that the new jQuery is already available.
Avoiding potential issues with that requires some callback magicks. First we edit injectjQuery, adding, immediately after the last line, the following code
@checkLoaded(saveOriginal)
checkLoaded is a function that keeps calling itself until jQuery is effectively loaded.
checkLoaded: (saveOriginal) -> window.setTimeout( => if !jQuery? @checkLoaded(saveOriginal) else if saveOriginal temp = @jq @jq = $.noConflict(true) window.jQuery = temp window.$ = jQuery else @jq = jQuery @continueInitialization() , 500)
It recognizes the same saveOriginal parameter we used before, restoring the original jQuery where it belongs, and leaving us with our new jQuery, available through this.jq.
continueInitializazion is the function that should handle all of your post-jquery-loaded needs.
In this gist you’ll find the complete Injector class, with some bonus console.log calls to better understand what’s going on.
Thank you for reading up to this last paragraph, and have fun injecting jQuery where you please.
Chrome and Opera (respectively from versions 20 and 12) implement a native date picker. Now, when you have an input field with the date
type:
<input type="date" name="user[born_on]" id="user_born_on" />
these two browsers will automatically inject in the page their native datepicker, according to the input[type=date] HTML5 specs.
And, at least in Chrome, the widget is really really good.
Btw, this doesn’t happen only for date fields. In fact in HTML5 we can find other new input types specifically created to deal with dates and times:
datetime
,datetime-local
,time
. Chrome and Opera will show a picker also for these input types.
There is only one little, negligible problem: as I said this feature is implemented only in Chrome and Opera!
Continue reading “Using HTML5 date fields mantaining backward compatibility”
Safari asks for your confirmation if you accidentally try to close a page if you filled a form but never submitted. This saved my day more than once and it’s undoubtedly helpful.
The problem is that if you’re sending the form via ajax Safari still thinks you’re loosing some data. Luckily the fix is easy:
var postBody = document.querySelector('textarea.post-body')
postBody.defaultValue = postBody.value
If you are a Ruby developer, you’re probably constantly complaining about Javascript’s quirks 🙂
Yes, Coffeescript is a great abstraction. It gives you a nice syntax that is both easier to read and easier to write. Stuff like the [fat arrow](Fat Arrow explaination) and the class keyword are undoubtfully better than their vanilla JavaScript counterparts. In addition Coffeescript compiles to plain Javascript without the need of any framework or third party library. In a nutshell, Coffeescript is the perfect tool when you need to write all that javascript glue to work with jquery plugins, backbone and all that stuff that we know well.
But when you need to write your own framework or to write a more complex app, even Coffeescript could not be the right tool.
Continue reading “Opal: give it a try”
I obeyed, therefore I wrote an opal
NPM package.
Now I can trash Rails and opal-rails
and start working on Node only!
server = Server.new 3000 server.start do [200, {'Content-Type' => 'text/plain'}, ["Hello World!n"]] end
For it I had to write the Server
class:
class Server def initialize port @http = `require('http')` @port = port end def start &block %x{ this.http.createServer(function(req, res) { var rackResponse = (block.call(block._s, req, res)); res.end(rackResponse[2].join(' ')); }).listen(this.port); } end end
Put them in app.opal
and then start the server:
$ npm install -g opal # <<< install the NPM package first! $ opal-node app
Cross posted from Eliæ
Cross posted from: Eliæ
Remeber that feeling that you had after having tried CoffeeScript getting back to a project with plain JavaScript and felt so constricted?
Well, after re-writing a couple of coffee classes with OpalRuby I felt exactly that way, and with 14.9KB of footprint (min+gz, jQuery is 32KB) is a complete no brainer.
So let’s gobble up our first chunk of code, that will salute from the browser console:
puts 'hi buddies!'
You wonder how that gets translated?
Fear that it will look like this?
'egin',cb='bootstrap',u='clear.cache.gif',z='content',bc='end',lb='gecko',mb='g cko1_8',yb='gwt.hybrid',xb='gwt/standard/standard.css',E='gwt:onLoadErrorFn',B= 'gwt:onPropertyErrorFn',y='gwt:property',Db='head',qb='hosted.html?hello',Cb='h ref',kb='ie6',ab='iframe',t='img',bb="javascript:''",zb='link',pb='loadExternal Refs',v='meta',eb='moduleRequested',ac='moduleStartup',jb='msie',w='name',gb='o pera',db='position:absolute;width:0;height:0;border:none',Ab='rel',ib='safari', rb='selectingPermutation',x='startup',m='hello',Bb='stylesheet',ob='unknown',fb ='user.agent',hb='webkit';var fc=window,k=document,ec=fc.__gwtStatsEvent?functi on(a){return fc.__gwtStatsEvent(a)}:null,zc,pc,kc,jc=l,sc={},Cc=[],yc=[],ic=[], vc,xc;ec&&ec({moduleName:m,subSystem:x,evtGroup:cb,millis:(new Date()).getTime( ),type:nb});if(!fc.__gwt_stylesLoaded){fc.__gwt_stylesLoaded={}}if(!fc.__gwt_sc riptsLoaded){fc.__gwt_scriptsLoaded={}}function oc(){var b=false;try{b=fc.exter
Nope! it’s Chuck Testa
(function() { return this.$puts("hi buddies!") }).call(Opal.top);
But it will be obviously a mess to integrate it with existing javascript!!1
Let’s how to add #next
and #prev
to the Element
class:
class Element def next(selector = '') element = nil `element = jQuery(this.el).next(selector)[0] || nil;` Element.new element if element end end
But I’m on Rails!
Here you served:
gem 'opal-rails'
Which il provide you a requirable libraries for application.js
//= require opal //= require rquery
and the .opal
extension for your files
# app/assets/javascripts/hi-world.js.opal puts "G'day world!"
A template handler:
# app/views/posts/show.js.opal.erb Element.id('<%= dom_id @post %>').show
and a Haml filter!
-# app/views/posts/show.html.haml %article= post.body %a#show-comments Display Comments! .comments(style="display:none;") - post.comments.each do |comment| .comment= comment.body :opal Document.ready? do Element.id('show-comments').on :click do Element.find('.comments').first.show false # aka preventDefault end end