When the iPhone first came out, there was no App Store, and Apple said developers should make webapps. Now, the debate between webapps and native apps will go on and on, but suffice it to say, sometimes developers will want to to code web apps that run on iOS devices, and sometimes platform-sepecific bugs will rear their ugly head.
Luckily the later versions of iOS have a great solution for this, at least for a developer who as a Mac; plug in the iPad or iPhone (even if it's not the usual one you synch to), fire up Safari on iOS, and then Safari on OSX. You may have enable the "Develop" menu if you haven't already, but then on that topbar menu you should see your device name, and a list of windows it can provide a console for. (As you select through the various available-to-debug menus you should see the corresponding window highlight in iOS Safari) You can then use that console window to view errors and do other DOM manipulation.
Going cross-device to debug is a pretty solid trick!
Wednesday, January 30, 2013
Friday, January 25, 2013
document.domain and iframe parent talk 101
Alleyoop relies on partner for a lot of its content which we embed in an iframe.
For some videos, we do the hosting outselves, and so we have a standalone Grails app that "pretends" to be a partner, just to keep everything nice and honest.
This current iteration involves upgrading that app so it can embed quiz questions in a sidebar. We're interested in how students respond to the new feature, so we have to add analytics inside the "partner" app.
I began copy and pasting the subset of code for analytics when I realized it wasn't as easy as it looked, our core analytics do some checking to see if this looks like a QA or developer whose data shouldn't "corrupt" the core stats, so the partner app would have to start to know way too much about the core app's user model and what not. (Especially challenging given how we deploy to Amazon's cloud service, so URLs might not be well known at coding time.)
With that in mind, I realized it would be easier and safer to create an analytics passthrough in the main app so that the "partner" app can talk through it, relying on the parent app to do all the heavy lifting.
The system relies on the fact that both the main app and the "partner" app live on the same domain (albeit on different subdomains) -- without that we'd probably be out of luck with this approach, because of the security restrictions on cross domain scripting.
So as a note to my future self, or other people doing something similar:
To start with, I had to update /etc/hosts so that the server on localhost could be called via the alleyoop.domain:
127.0.0.1 grails.alleyoop.com
Then as a Proof of Concept, I made the following iframe in the parent app:
<script>
document.domain = "alleyoop.com";
function gotIt(){
alert("!!!got it!!!!");
}
</script>
<iframe width="100%" height="100%"
src="http://grails.alleyoop.com:8888/AlleyoopVideoPartner/">
</iframe>
Then in the "partner" app, near the top:
<script>
document.domain = "alleyoop.com";
window.parent.gotIt();
</script>
And that worked. "document.domain" is kind of the secret sauce, it has to be the same if the two pages are going to communicate. Note that you can't set "document.domain" to whatever you want, it is constrained by the domain where the page is actually living.
I'm glad it worked! My Plan B was a terrible cookie hack and poller...
BONUS PROTIP: A few days later and I ran into the above code not working... it was because of an http/https mismatch in the page and the content of the iframe. If I wasn't so tired it wouldn't have taken me so long to figure out!
For some videos, we do the hosting outselves, and so we have a standalone Grails app that "pretends" to be a partner, just to keep everything nice and honest.
This current iteration involves upgrading that app so it can embed quiz questions in a sidebar. We're interested in how students respond to the new feature, so we have to add analytics inside the "partner" app.
I began copy and pasting the subset of code for analytics when I realized it wasn't as easy as it looked, our core analytics do some checking to see if this looks like a QA or developer whose data shouldn't "corrupt" the core stats, so the partner app would have to start to know way too much about the core app's user model and what not. (Especially challenging given how we deploy to Amazon's cloud service, so URLs might not be well known at coding time.)
With that in mind, I realized it would be easier and safer to create an analytics passthrough in the main app so that the "partner" app can talk through it, relying on the parent app to do all the heavy lifting.
The system relies on the fact that both the main app and the "partner" app live on the same domain (albeit on different subdomains) -- without that we'd probably be out of luck with this approach, because of the security restrictions on cross domain scripting.
So as a note to my future self, or other people doing something similar:
To start with, I had to update /etc/hosts so that the server on localhost could be called via the alleyoop.domain:
127.0.0.1 grails.alleyoop.com
Then as a Proof of Concept, I made the following iframe in the parent app:
<script>
document.domain = "alleyoop.com";
function gotIt(){
alert("!!!got it!!!!");
}
</script>
<iframe width="100%" height="100%"
src="http://grails.alleyoop.com:8888/AlleyoopVideoPartner/">
</iframe>
Then in the "partner" app, near the top:
<script>
document.domain = "alleyoop.com";
window.parent.gotIt();
</script>
And that worked. "document.domain" is kind of the secret sauce, it has to be the same if the two pages are going to communicate. Note that you can't set "document.domain" to whatever you want, it is constrained by the domain where the page is actually living.
I'm glad it worked! My Plan B was a terrible cookie hack and poller...
BONUS PROTIP: A few days later and I ran into the above code not working... it was because of an http/https mismatch in the page and the content of the iframe. If I wasn't so tired it wouldn't have taken me so long to figure out!
Thursday, January 17, 2013
cool fixed backgrounds in CSS
Deciding to practice what I preach, I dive into a cool but simple CSS effect as used in that recent
The Verge article on Arcades:
Tuesday, January 15, 2013
paste images right into chrome!
OK,
this, despite some serious limitations, is right-sexy, "browsers can do that now?" stuff.
I found a page that has a jQuery "image paste" plugin -- it's chrome only, but you can copy an image in an outside program into clipboard and paste it into a DOM element. That will give you a base64-encoded version of the image data that you can then use as as the src to an img tag, or the background-image property of a div. Here's an example! (chrome only)
.
The page providing the plugin isn't 100% clear of its use, especially if you're not using CoffeeScript, so here's the upshot:
Obviously you need a reference to the script itself:
<script src="http://kirk.is/m/files/kirkdev/imagepaste/jquery.paste_image_reader.js"></script>
Then in the document ready,call pasteImageReader() on the jQuery selector, and pass in a callback function
$(".pasteZone").pasteImageReader(loadIt);
That call back function will then be passed a hash. One of the keys of the hash will "dataURL". You can then use dataURL as the src for an img tag, or as the background-image of a div or somesuch...
$(".pasteImg").attr("src",res.dataURL);
$(".pasteZone").css("backgroundImage", "url("+res.dataURL+")");
That's about it! There are some issues to using Base64-encoded images, like they won't be cached and aren't generally as efficient as external files, but in some circumstances they are darn nifty! This stackoverflow discusses browser compatibility; IE earlier than 8 is out of luck with displaying this type of image. (A simple PHP script or similar could be used to convert a base64 image into a literal file.)
I found a page that has a jQuery "image paste" plugin -- it's chrome only, but you can copy an image in an outside program into clipboard and paste it into a DOM element. That will give you a base64-encoded version of the image data that you can then use as as the src to an img tag, or the background-image property of a div. Here's an example! (chrome only)
.
copy image, click here and hit ctrl-v...
result:
The page providing the plugin isn't 100% clear of its use, especially if you're not using CoffeeScript, so here's the upshot:
Obviously you need a reference to the script itself:
<script src="http://kirk.is/m/files/kirkdev/imagepaste/jquery.paste_image_reader.js"></script>
Then in the document ready,call pasteImageReader() on the jQuery selector, and pass in a callback function
$(".pasteZone").pasteImageReader(loadIt);
That call back function will then be passed a hash. One of the keys of the hash will "dataURL". You can then use dataURL as the src for an img tag, or as the background-image of a div or somesuch...
$(".pasteImg").attr("src",res.dataURL);
$(".pasteZone").css("backgroundImage", "url("+res.dataURL+")");
That's about it! There are some issues to using Base64-encoded images, like they won't be cached and aren't generally as efficient as external files, but in some circumstances they are darn nifty! This stackoverflow discusses browser compatibility; IE earlier than 8 is out of luck with displaying this type of image. (A simple PHP script or similar could be used to convert a base64 image into a literal file.)
Sunday, January 13, 2013
quick prototype and html5 video fun
So we're experimenting with new assessment methods, including ones that are tightly linked to certain time spots in educational videos. So to ask questions in videos, you need an editor, and this is a prototype I whipped up in a few hours...it's based on flowplayer, a great HTML5 and Flash video player. It has a decent API, and the documentation is decent, if a bit uneven (and Google kind of points to a lot of the older, Flash-oriented stuff.)
It was a fun little exercise in UI design; you can click a button, it sets a timestamp on the timeline, and you can enter questions, and then come back and edit the question for a specific time.
So nothing too exciting, just here to show off a few hours work and as reference to my future self.
It was a fun little exercise in UI design; you can click a button, it sets a timestamp on the timeline, and you can enter questions, and then come back and edit the question for a specific time.
So nothing too exciting, just here to show off a few hours work and as reference to my future self.
Tuesday, January 8, 2013
prototyping a spotlight effect
At work we're tossing around some ideas about motivating users with "surprise and delight" (which sounds better than "randomness and chaos").
I've been advocating for a form of random token rewards, where part of the charm is that there are enough of them (and hopefully they're visually interesting enough) that they provide a mild motivation to get more of them. Unlike normal badges, it's not just a pride thing, but these might be able to work a Pokemon-ish "gotta catch 'em all!" effect.
I wanted to create an interaction for showing a user "hey this is the one you won" while hinting at the others that they almost won. My first thoughts were similar to the Simple Slot Machine Effect I posted earlier, though maybe with a horizontal layout rather than vertical. On further thought, I came up with a "spotlight" where a roaming spotlight lands on the one they get, and I hacked together a quick prototype:
(click to get a new set of images and rerun the spotlight)
For the trophy doodles, I used a sheet of doodles I made a while back. The spotlight effect is straightforward: there is a "stage" outer div with its overflow hidden, and it holds a single sheet of all the doodles (good enough for the prototype) and a mask div with a transparent circle in the middle, and the rest displayed gray with a 90% opacity.
The motion of the spotlight is more complex; I used some techniques I use all the time in my homebrew games, giving the spot and x and y speed, and then over a series of timeouts I use that to adjust the offset of the mask layer. (The x speed and y speed are adjusted so the spotlight is pushed towards the center each click, if the light is going to be outside the "stage" it rebounds and loses some speed, the speed is throttled just a bit, and then finally if the thing is close enough to the center and moving slowly enough, it won't get moved again to prevent any "near the center but not quite" jitter.)
Coworkers pointe out it might be cooler to use more variable translucency, so it's even darker farther away from the spotlight, and of course with the real thing it wouldn't be a single sheet. Still it gets the idea across rather well, and only took an hour or so to churn out.
I've been advocating for a form of random token rewards, where part of the charm is that there are enough of them (and hopefully they're visually interesting enough) that they provide a mild motivation to get more of them. Unlike normal badges, it's not just a pride thing, but these might be able to work a Pokemon-ish "gotta catch 'em all!" effect.
I wanted to create an interaction for showing a user "hey this is the one you won" while hinting at the others that they almost won. My first thoughts were similar to the Simple Slot Machine Effect I posted earlier, though maybe with a horizontal layout rather than vertical. On further thought, I came up with a "spotlight" where a roaming spotlight lands on the one they get, and I hacked together a quick prototype:
(click to get a new set of images and rerun the spotlight)
The motion of the spotlight is more complex; I used some techniques I use all the time in my homebrew games, giving the spot and x and y speed, and then over a series of timeouts I use that to adjust the offset of the mask layer. (The x speed and y speed are adjusted so the spotlight is pushed towards the center each click, if the light is going to be outside the "stage" it rebounds and loses some speed, the speed is throttled just a bit, and then finally if the thing is close enough to the center and moving slowly enough, it won't get moved again to prevent any "near the center but not quite" jitter.)
Coworkers pointe out it might be cooler to use more variable translucency, so it's even darker farther away from the spotlight, and of course with the real thing it wouldn't be a single sheet. Still it gets the idea across rather well, and only took an hour or so to churn out.
Subscribe to:
Posts (Atom)