I'm working on a site where I'd like to cycle images, similar to a slideshow, while the user is on the page. I've searched around and haven't been able to find a lead.
Has anyone done this with Rails and the Javascript frameworks it supports?
you could possible use the jquery cycle plugin, here's the link: http://malsup.com/jquery/cycle/ . It looks like it would do what you want.
FYI, Rails isn't particularly tied to any JS framework, even though it comes with prototype out of the box.
I assume that when you say AJAX, you don't mean AJAX. If you were to rotate the image using AJAX, you would have the rotated image being generated server side and sent to the client, as AJAX = performing requests with javascript. </pedantic>
With that said, you can use pretty much any JS image rotator you can find on google.
EDIT: Oh, you meant cycling through images, not rotating it e.g. 90º clockwise etc. (?)
I've been using this very simple function. It doesn't use any framework and it doesn't fade between slides, but it should get you started.
function SlideShow( elem_id, hold_time )
{
this.elem = document.getElementById( elem_id );
this.slides = [];
this.num_slides = 0;
this.cur_slide = 1;
this.add_slide = function( image )
{
this.slides[ this.num_slides++ ] = image;
}
var self = this;
this.next_slide = function()
{
if ( self.num_slides > 1 )
{
self.elem.src = self.slides[ self.cur_slide++ ].src;
if ( self.cur_slide == self.num_slides )
self.cur_slide = 0;
}
}
setInterval( self.next_slide, hold_time )
}
the parameters are the element_id of an img tag and the number of mS to display each slide.
the add_slide function takes a JavaScript Image object.
The reason cur_slide is initialised to 1 is because I pre load the img tag with the first image.
In my application I create the slideshow in the window.onload method and arrange for each Image to add itself to the slide show as it is loaded.
Example (untested):
window.onload = function() {
var slide_show = new SlideShow( "slide_image", 4000 )
{ var img = new Image();
img.onload = function(){ slide_show.add_slide(img); };
img.src="/images/slide1.jpg"; }
...
/* Repeated for each image */
}
This approach is only valid if you don't care about the order of the slides.
I assume you want to make persistent changes on images. I'm working on a Rails app where we implemented resize, crop and rotate similar to features in snipshot.com
On technical side we used YUI for resize and crop in user interface and RMagick on server to process the images and send the results back to UI. YUI provides imagecrop widget out-of-the box.
We also considered doing a series of actions in UI and then submit the last result for server-side processing but that would have been lead to inadequate results.
Related
I am trying to make a basic Instagram web scraper, both art inspiration pictures and just generally trying to boost my knowledge and experience programming.
Currently the issue that I am having is that Casper/Phantomjs can't detect higher res images from the srcset, and I can't figure out a way around this. Instagram has their srcsets provide 640x640, 750x750, and 1080x1080 images. I would obviously like to retrieve the 1080, but it seems to be undetectable by any method I've tried so far. Setting the viewport larger does nothing, and I can't retrieve the entire source set through just getting the HTML and splitting it where I need it. And as far as I can tell, there is no other way to retrieve said image than to get it from this srcset.
Edit
As I was asked for more details, here I go. This is the code I used to get the attributes from the page:
function getImages() {
var scripts = document.querySelectorAll('._2di5p');
return Array.prototype.map.call(scripts, function (e) {
return e.getAttribute('src');
});
}
Then I do the standard:
casper.waitForSelector('div._4rbun', function() {
this.echo('...found selector ...try getting image srcs now...');
imagesArray = this.evaluate(getImages);
imagesArray.forEach(function (item) {
console.log(item);
However, all that is returned is the lowest resolution of the srcset. Using this url, for example, (https://www.instagram.com/p/BhWS4csAIPS/?taken-by=kasabianofficial) all that is returned is https://instagram.flcy1-1.fna.fbcdn.net/vp/b282bb23f82318697f0b9b85279ab32e/5B5CE6F2/t51.2885-15/s640x640/sh0.08/e35/29740443_908390472665088_4690461645690896384_n.jpg, which is the lowest resolution (640x640) image in the srcset. Ideally, I'd like to retrieve the https://instagram.flcy1-1.fna.fbcdn.net/vp/8d20f803e1cb06e394ac91383fd9a462/5B5C9093/t51.2885-15/e35/29740443_908390472665088_4690461645690896384_n.jpg which is the 1080x1080 image in the srcset. But I can't. There's no way to get that item as far as I can tell. It's completely hidden.
I found a way around it in Instagram's case. Instagram puts the source picture in a meta tag within the head. So, using the code I'll paste below, you can call all of the meta tags and then sort out which one is the source picture by checking if "og:image" is retrieved.
function getImages() {
var scripts = document.querySelectorAll('meta[content]');
return Array.prototype.map.call(scripts, function (e) {
return e.getAttribute('property') + " " + e.getAttribute('content');
});
}
And this is the way to sort the meta tags into only having the original image in its native resolution.
this.echo('...found selector ...try getting image srcs now...');
imagesArray = this.evaluate(getImages);
imagesArray.forEach(function (item) {
if (typeof item == "string" && item.indexOf('og:image') > -1) {
Edit: Unfortunately this only works for single image posts on Instagram (the site I'm trying to scrape) so this unfortunately does me no goo. The values within the meta tags don't change even if you load the next image in the post. I'm leaving this up though in case anyone else could use it, but it's not ideal for my own use case.
Yes indeed PhantomJS doesn't seem to support srcset, its Webkit engine is very old.
But to be fair, all the metadata related to the page is out in the open in the HTML as JSON in window._sharedData variable.
If you want to use a headless browser (and not parse it with any server-side language) you can do this:
var imgUrl = page.evaluate(function(){
return window._sharedData.entry_data.PostPage[0].graphql.shortcode_media.display_resources[2].src;
});
https://instagram.fhen2-1.fna.fbcdn.net/vp/8d20f803e1cb06e394ac91383fd9a462/5B5C9093/t51.2885-15/e35/29740443_908390472665088_4690461645690896384_n.jpg
Solution: So my solution was to use slimerjs. If I run the js file through "casperjs --engine=slimerjs fileName.js", I can retrieve srcsets in full. So if I say use this code:
function getImgSrc() {
var scripts = document.querySelectorAll("._2di5p");
return Array.prototype.map.call(scripts, function (e) {
return e.getAttribute("srcset");
});
}
on this url (https://www.instagram.com/p/BhWS4csAIPS/?taken-by=kasabianofficial) I will get (https://instagram.flcy1-1.fna.fbcdn.net/vp/b282bb23f82318697f0b9b85279ab32e/5B5CE6F2/t51.2885-15/s640x640/sh0.08/e35/29740443_908390472665088_4690461645690896384_n.jpg 640w,https://instagram.flcy1-1.fna.fbcdn.net/vp/b4eebf94247af02c63d20320f6535ab4/5B6258DF/t51.2885-15/s750x750/sh0.08/e35/29740443_908390472665088_4690461645690896384_n.jpg 750w,https://instagram.flcy1-1.fna.fbcdn.net/vp/8d20f803e1cb06e394ac91383fd9a462/5B5C9093/t51.2885-15/e35/29740443_908390472665088_4690461645690896384_n.jpg 1080w) as the result.
This is what I wanted as it means I can scrape those 1080 images. Sorry for this messy page, but I wanted to leave my trail of steps to any of those who might be trying like me.
All of the assets of the site are in an array of objects that will be shown after user interaction, something like a slider.
{"id":"1","icon":"icon-name-class","sound":"soud-name"},
{"id":"2","icon":"icon-2-name-class","sound":"soud-2-name"},
....
{"id":"100","icon":"icon-100-name-class","sound":"soud-100-name"}
I have about 150 small icons and around 100 small sounds that need to be preloaded, been looking and didn't find anything related to preload assets from a js array.
I'm using the images as a background and getting the class name from the object.
I thought that all images would be ready since they are coming from a css class, but i guess that i am wrong
.icon-class-1 { background-image: url(../img/icons/img-1.png); }
this link or something similar to this could do the trick but after reading the doc couldn't find how to apply it when the assets origin is in an array that will be managed with js.
You have to loop through the object array to load them.
EDIT
Since you have a lot images which doesn't load via CSS.
I suggest you remove all these backgroung-image:url('...'); and put these URLs in your json like this:
{"id":"1","icon":"icon-name-class", "icon_url":"http://domain/path/file.jpg", "sound":"sound-name","sound_url":"http://domain/path/file.mp3"},
And do the same for your sound urls.
PLUS! It will ease the maintenance in the long run... If you notice a broken URL someday.
Then, preloading can be made like this:
var object_array = [
{"id":"1","icon":"icon-name-class", "icon_url":"http://domain/path/file1.jpg", "sound":"sound-name","sound_url":"http://domain/path/file1.mp3"},
{"id":"2","icon":"icon-name-class", "icon_url":"http://domain/path/file2.jpg", "sound":"sound-name","sound_url":"http://domain/path/file2.mp3"},
{"id":"3","icon":"icon-name-class", "icon_url":"http://domain/path/file3.jpg", "sound":"sound-name","sound_url":"http://domain/path/file3.mp3"}
];
// Preloading arrays
var icon_url_arr = [];
var sound_url_arr = [];
// Load counter
var loaded_counter={icon:0,sound:0};
function increment_counter(x){
loaded_counter[x]++;
}
// Loop through the data array
for(i=0;i<object_array.length;i++){
if(typeof(object_array[i].icon_url)!="undefined"){
icon_url_arr[i] = $("<img>").attr("src",object_array[i].icon_url).on("load",function(){increment_counter("icon");});
}
if(typeof(object_array[i].sound_url)!="undefined"){
var audio_element = $("<audio>").attr("id","audio_"+i);
sound_url_arr[i] = $("<source>").attr("src",object_array[i].sound_url).attr("type","audio/mpeg");
audio_element.append(sound_url_arr[i]).on("loadeddata",increment_counter("sound"));
$("#main").append(audio_element);
}
}
// Interval to check load status
var check_all_loaded = setInterval(function(){
console.log("Loded icons: " +loaded_counter.icon+ " Loaded sounds: " +loaded_counter.sound );
$("#icon").html((loaded_counter.icon/icon_url_arr.length)*100+"%");
$("#sound").html((loaded_counter.sound/sound_url_arr.length)*100+"%");
// Stop the interval when all loaded
if( (icon_url_arr.length==loaded_counter.icon) && (sound_url_arr.length==loaded_counter.sound) ){
clearInterval(check_all_loaded);
console.log("Load check Interval stopped.");
// Now you can use the loaded icons and sounds
cycle_data();
}
},10);
// cycle through data on interval
function cycle_data(){
console.log("Display Interval started.");
// Background
var i=0;
setInterval(function(){
$("#main").css("background-image","url("+icon_url_arr[i].attr("src")+")");
// Audio
$(document).find("audio").get(0).pause();
$(document).find("#audio_"+i).get(0).play();
// Increment loop counter.
i++;
// Reset counter when the array end is reached
if(i==icon_url_arr.length){
i=0;
}
},2000);
}
So it won't be displayed or played anywhere...
But the browser will have it loaded.
You may have to hide the audio players via CSS:
audio{
display:none;
}
I made a CodePen demo to make sure it is working.
There is only 3 items in json...
And images and sounds are pretty small.
So you won't really notice the load delay in this demo.
;)
I suggest you place this script in a document.ready() wrapper...
And within a setTimeout() function to execute it something like 0.5 to 2 seconds after document is ready.
So the rest of your page won't lag.
I need to do a simple pdf with some 3D objects for an oral presentation. I made several views, each one with a camera viewpoint, an object and a display mode.
To avoid the need to manually switch between the different viewpoints with the context menu, I would like the viewpoints to switch automatically with a Timer (each viewpoint staying for a few seconds). And I would like to not have to touch the mouse at all (nor the keyboard), so I would like to have the playback started as soon as the page appears.
I found the javascript command runtime.setView(N,x) to switch to the x'th view among N, but I don't know where to put it (I don't want to define a function which will be called when I press a button, since I want everything to be automated). Also, I don't know how to pause for a few seconds.
Any help ? Thanks !
I believe you're looking for setInterval(fn, time) which will call a function periodically at a time interval that you set. I'm not familiar with the setView() method you mentioned, but here's some pseudo code that you would put in tags at the end of the document body.
function startSwitcher()
var viewNum = 0;
var maxViews = 5; // you set this to how many views there are
setInterval(function() {
++viewNum;
if (viewNum >= maxViews) {
viewNum = 0;
}
runtime.setView(N, viewNum); // you will have to figure out this line
}, 2000);
}
startSwitcher();
The 2000 is 2000 milliseconds and is the time interval between executing the function. You can put any number of milliseconds there.
The runtime.setView(N, viewNum) line is something you will have to figure out as I am not familiar with whatever library you're trying to use there. The operative part of this code is the viewNum variable which configures which view in rotation should be next.
I think the runtime.SetView(..) Methods works with the name of the view as a string instead of the viewnumber. I have this function in a Dokument-level script and it works for me:
// view is the name of the view for example "TopView"
function setView(view){
console.println("Navigating to view: "+view);
var pageIndex = this.pageNum;
var annotIndex = 0;
var c3d = this.getAnnots3D( pageIndex )[ annotIndex ].context3D;
c3d.runtime.setView(view, true);
}
Combine this with the setInterval(..) from jfriend00´s answer und you should get what you need.
Best regards
I' m writing a web app that needs to load dynamically a lot of images.
I wrote an utility function, loadMultipleImages, that takes care of loading them and calling (possibly optional) callbacks whenever:
a single image is loaded
an error is encountered (a single image can't load)
all the images are loaded without errors
I invoke it like this (you can find a complete example here):
var imageResources = [];
var mulArgs = {multipleImages: [],
onComplete: this.afterLoading.bind(MYAPP),
onError: this.logError.bind(MYAPP)
}
imageResources = ["imageA_1.png", "imageA_2.png"];
mulArgs.multipleImages.push ({ID: "imageA_loader", imageNames : imageResources});
imageResources = ["imageB_1.png", "imageB_2.png"];
mulArgs.multipleImages.push ({ID: "imageB_loader", imageNames : imageResources});
//Lots of more images
var mImageLoader = new loadMultipleImages (mulArgs);
As soon as I create my loadMultipleImages object, it loads the images and calls the afterLoading() function after they are all loaded (or logError() if there's some problem). Then I can access to the images with:
MYAPP.afterLoading = function (loaders) {
// Just an example
var imageA_array = loaders["imageA_loader"].images;
var imageB_first = loaders["imageB_loader"].images[0];
}
By the way, I'm thinking that I'm reinventing the (possibly square) wheel. Is there a lightweight, simple library that does that better than I'm doing? Or simply a better method that spares me the burden of maintaining the loadMultipleImages code?
http://jsfromhell.com/classes/preloader
I want to display little messages to provide feedback to the user while he
is providing input or just interacting with the UI.
I need it for my firefox addon, so I have to develop it in plain javascript
and not jQuery.
I want the message to appear, but only one message can be visible at the same
time, so I need some kind of queue to manage incomming messages. After a certain time
e.g. 3 sec the message should fade away or just disappear.
For now I am able to add messages to the DOM. Any suggestions how to implement the queue
and how to push the messages forward according to the time?
Thanks!
Perheps you need the concept of FIFO (First In First Out)
Take a look at this simple example in plan java script language:
function Queue() {
var data = [];
this.isEmpty = function() {
return (data.length == 0);
};
this.enqueue = function(obj) {
data.push(obj);
};
this.dequeue = function() {
return data.shift();
};
this.peek = function() {
return data[0];
};
this.clear = function() {
data = [];
};
}
You can use jQuery in a firefox plugin:
Include a script tag in the xul file that points to the jQuery file, e.g.:
<script type="text/javascript" src="chrome://extensionname/content/jquery.js" />
In each js function that uses jQuery, insert this line:
$jQuizzle = jQuery.noConflict();
In each jQuery call, if you are trying to manipulate the document in the current browser window, you must supply the context as "window.content.document", like this:
$jQuizzle(".myClass", window.content.document).show();
Then you can use this jQuery plugin:
http://benalman.com/projects/jquery-message-queuing-plugin/
It's not clear what sort of message you want to display. The nsIAlertsService can display messages but I'm not sure how well it queues them. If you want something simpler then perhaps you could just show a custom <tooltip> element.