JavaScript


Fastest method for Javascript substring search

I have a mobile application that needs to search through a rather large array and I’m experiencing some optimization issues with it.  So, I decided to run some tests to see which function would be the fastest between indexOf, search, and includes.

I first tested all three with a static search value, search performed the best:

test1

But, with a variable, indexOf performed the best:

test2

I was able to perform these tests pretty easily using https://jsperf.com/ :)

Permalink » No comments

Compress images with simple smusher

I wrote a similar post using TinyPNG about a week ago, but the 500 image limit was a bit small so I built my own very simple image compressor.  So I’ve updated the code to work with my simple smusher, added a few improvements, and I have a PHP version as well.

Ruby Version

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
require 'open-uri'
require "json"
require "net/http"
require "uri"
require 'rubygems'
 
start = (ARGV[0] ? ARGV[0] : 0).to_i
last = start+100
i = 0
total_savings = 0
Dir.glob("public/system/rich/rich_files/rich_files/**/*.{jpg,png}") do |item|
  #only do 100 images at a time
  break if i == last
 
  #ignorre directories and original files
  next if item == '.' or item == '..' or item.include? "original"
  i = i + 1
  next if i <= start
 
  url = item.sub 'public', 'http://www.jimstoppani.com'
  uri = URI.parse("http://yourdomain.com/?i=#{url}")
  http = Net::HTTP.new(uri.host, uri.port)
  http.read_timeout = 500
  request = Net::HTTP::Get.new(uri.request_uri)
  response = http.request(request)
 
  if response.code == "200"
    result = JSON.parse(response.body)
  else
    break
  end
 
  puts "Converted: #{item}"
 
  if result[0]['dest_size'] < result[0]['src_size']
    open(item, 'wb') do |file|
      file << open(result[0]["dest"]).read
    end
    puts "Original: #{result[0]['src_size']} Optimized #{result[0]['dest_size']}"
    total_savings = total_savings + (result[0]['src_size']-result[0]['dest_size'])
  else
    puts "no size difference"
  end
end
 
puts "All done! Total Savings: #{total_savings}"

This one has a few advantages over the last version – it needs a variable sent with it so it doesn’t run into the problem of re-compressing images that have already been compressed.  It does 100 images at once, so you could run it in a loop like “ruby tinify.rb 0″, “ruby tinify.rb 100″, “ruby tinify.rb 200″ ect.

It also checks the response from the compressor, and it there’s no file size savings it doesn’t copy over the new version. And once it’s done, it will tell you the total savings.

PHP Version

HTML:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Photo Smusher</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
 
    <script>
        $(function(){
        var total = 0;
        var start = 0;
 
 
        function smush_it() {
            var jqxhr = $.getJSON("smush.php?img=FOLDER&p="+start, function(data) {
              total = total + parseFloat(data[0].total);
              start = start+5;
              $("#total").text(total);
              $("#start").text(start);
              if(start < 5010) {
                  smush = setTimeout(function(){smush_it()},60000);
              }
            }).fail(function( jqxhr, textStatus, error ) {
                var err = start + ' ' + textStatus + ", " + error;
                smush = setTimeout(function () {
                    smush_it()
                }, 60000);
                $("#error").text(err);
            });
        }
 
        $("#total").text(total);
        $("#start").text(start);
        smush_it();
 
        });
    </script>
</head>
 
<body>
    <p>Progress: <span id="start"></span></p>
    <p>KB saved: <span id="total"></span></p>
    <p id="error"></p>
</body>
</html>

PHP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?php
function loadFile($url) {
    $ch = curl_init();
 
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_URL, $url);
 
    $data = curl_exec($ch);
    curl_close($ch);
 
    return $data;
}
 
 
function downloadfile($file, $path, $fn) {
    if(isset($file) && isset($path) && isset($fn)) {
        $fc = implode('', file($file));
 
        $mode = (file_exists($path . $fn) ? 'w': 'x+');
        $Files = fopen($path . $fn, $mode);
 
        if ((fwrite($Files, $fc)) != 0){
            fclose($Files);
        } else{
            echo 'Error.';
        }
    }
}
 
if(isset($_REQUEST['img']) && isset($_REQUEST['p']) && is_numeric($_REQUEST['p'])) {
   $total = 0;
   $limit = 5;
   $start = $_REQUEST['p'];
   $end = $start + $limit;
   $item = 0;
 
   $handle = opendir('../images/'.$_REQUEST['img']);
   while ($file = readdir($handle)){
      if($item >= $start && $item <= $end) {
         $extension = strtolower(substr(strrchr($file, '.'), 1));
         if($extension == 'jpg'){
            $image = json_decode(loadFile('http://yourdomain.com/?i=http://yourdomain.com/images/'.$_REQUEST['img'].'/'.$file));
 
            $change = $image[0]->src_size-$image[0]->dest_size;
 
            if($change > 1) {
               $total += $change;
               downloadfile($image[0]->dest,'../images/'.$_REQUEST['img'].'/',$file);
            }
         } 
      } elseif($item > $end) {
         break;
      }
      $item++;
   } 
 
   echo '[{"total":"'.$total.'"}]';
}
 
?>

The HTML page goes through images in the FOLDER you designate, 5 at a time until you hit image 50010, which you can customize based on your folder size. It sends an ajax request to the php file, then reports back the total KB saved.  It will run the script once a minute 60000, which you can change depending on your server’s performance.

Once the PHP page receives a request it makes sure it has all the required request variables, sets up the counters, then goes through all the images in the designated folder.

The compressor will handle php and gif files, but I only had jpgs in this project so I limited it to only process jpgs.  So once it find a jpg it will send the full URL to your image compression service, you can replace “yourdomain.com” with the url of your service.  Then it decodes the json, and if there was any compression in the file size it will download the new image over the old one.  And once it’s gone through 5 images will report the total KB saved back to the HTML file.

 

Permalink » No comments

Social Login with Phonegap, Omniauth, Tokens, and InAppBrowser

For a project I needed to integrate Facebook and Twitter logins to a phonegap app.  I went through a ton of plugins and jsOAuths, but they were all quite cumbersome. I really wanted a simpler approach that would handle Twitter and Facebook at the same time.

This article about Twitter integration with Child Browser got me started in the right direction.  It’s a little dated with Child Browser, so I set out to update it to work with InAppBrowser, but during the process I realized that I could actually handle this issue a lot simpler.  Mostly because I only needed to login and didn’t require any deeper integration with Social platforms.

So after a little more digging I came across this lovely article on cross window communication with InAppBrowser which was everything I needed to take care of business!

In my case, the server is going to be doing all the heavy lifting, and for this project we already had Omniauth set-up for social login and tokens so I only needed to make a few tweaks to get this rolling.

The first tweak was to get it to output JSON for the App.  Out of the box Omniauth will keep track of your params, so I made it return a JSON response when I requested it in the query string like so “/users/auth/facebook?json=true”

Just for example, here how it would look if you were using the standard facebook action for omniauth:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def facebook
    @user = User.from_omniauth(request.env["omniauth.auth"])
 
    if @user.persisted?
      sign_in @user, :event => :authentication #this will throw if @user is not activated
    else
      session["devise.facebook_data"] = request.env["omniauth.auth"]
      return redirect_to new_user_registration_url
    end
 
    return render :json => current_user if request.env["omniauth.params"]["json"]
 
    set_flash_message(:notice, :success, :kind => "Facebook") if is_navigational_format?
    redirect_to "/"
  end
end

The important parts here are:

  • I’m using “sign_in” instead of “sign_in_and_redirect” so I can handle the redirection later down the line.
  • Then I’m adding in “return render :json => current_user if request.env[“omniauth.params”][“json”]” so the callback will output the current user in JSON.

In my case the JSON output for current_user includes a token I can use to integrate with the API.

With that all set you’re ready to capture that JSON in phonegap:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
win = window.open(base_url+'/users/auth/facebook?json=true', '_blank', 'location=yes,enableViewPortScale=yes');
win.addEventListener('loadstop', function (e) {
  if (e.url.indexOf(base_url) >= 0 && e.url.indexOf('callback') >= 0) {
    win.executeScript(
      { code: "document.body.innerHTML" },
      function(values) {
        r = values[0];
        r = r.replace(r.substring(0, r.indexOf('{')),"").replace(r.substring(r.indexOf('}'),r.length).replace(/}/g,''),"");
        r = jQuery.parseJSON(r);
        //now r.api_token is available to use however you need in your app
        win.close();
      }
    );
  }
});

This works by:

  1. First opening a window with the social login page “/users/auth/facebook?json=true
    on my app base_url is the domain of the API, like http://api.google.com for example.
  2. Next I’m listening for every-time the page in InAppBrowser finishes loading with “addEventListener(‘loadstop‘”
  3. Then I’m waiting for the URL to have my API’s domain name plus “callback” here “if (e.url.indexOf(base_url) >= 0 && e.url.indexOf(‘callback’) >= 0) {
  4. Once there I use “executeScript” to fetch the document body, which is returned as values[0].
  5. Then I clean the return value of any HTML using replace and substring, then parse that to JSON

After that you can use that token in your App however you need to integrate with your API.

Permalink » No comments

350s gallery

I randomly made this little webpage to showcase a lot of 350×200 openers I made for online articles :)

It’s a pretty simple script built on top of jquery:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<style type="text/css">
body {
    margin:0;
    overflow:hidden;
}
</style>
<script type="text/javascript">
$(function(){
    $("div").mousemove(function(e){
      height = $(window).height();
        width = $(window).width();
        percent = e.clientY * 100 / height;
        move = ($("#box").height()-height) * percent / 100;
        $("body").scrollTop(move);
 
        percent2 = e.clientX * 100 / width;
        move2 = ($("#box").width()-width) * percent2 / 100;
        $("body").scrollLeft(move2);
    });
});
</script>

The CSS hides the scrollbars and removes the default margins from the body.

Then the Javascipt adds an event handler that will fire every time the mouse moves on top of a div – which is always since the page is pretty much just one giant div.  After that it determines what percent of the window you cursor is currently sitting at, and then translates that to the same percentage of the size of that giant div and scrolls the window accordingly.  So regardless of the size of the window you’ll be able to move it around to see all the contents of the div.

Scroll around the box below to see the random goodness:

Permalink » No comments

Javascript Zoom In

Tags: , , ,

For a project I wrote this JavaScript based image zoom in application.   At the time I couldn’t find anything out there that I could use to get the desired effect (at least w/out shelling out some dough) – so I opted to write something myself.

The entire idea was not well received…so I never actually finished it.   So this is essentially just a “proof of concept”.  It demonstrates that you can zoom in, zoom out, and move an image around within a specified area without the use of flash.  I don’t know if I’ll ever get the chance to finish this, or really even have a reason to finish, but so it doesn’t get lost I thought I would post it up here.

Regardless of the outcome it was a good exercise in JavaScript :)

Click here to see it in action!

The zoom icons used in this are from Dry Icons

Permalink » No comments