Showing posts with label Jquery. Show all posts
Showing posts with label Jquery. Show all posts

Monday, May 26, 2014

SoundManager2, jquery, angular and the Hotline Miami OST - Part 1

Sound Manager 2
I haven't written anything in the last 30 days. I've been a busy and lazy (among other things). But I had to write these things down before I forget again. I'm planning to do this in 2 parts so it won't be that long to read.

Sound Manager 2
Sound Manager 2 is this JavaScript library simplify playing audio on a web page. Think embedded players like SoundCloud and such. It's pretty nifty with fail-over support for Mp3 via Flash if you somehow have a retarded web browser who can't play an HTML5 audio tag. The whole point is you end up with a single API to use to play audio files IF only it didn't have a few gotchas.

The SM2 Gotchas that got me in the ass
  • Read the requirements page closely. Don't do what I did and dove straight into the API and got hung up on a few issues (some Flash crap and arbitrary seeking) which could have been resolved quite easily and quickly if I have READ THE FUCKING REQUIREMENT'S PAGE a bit more closely. 
  • Variable Bitrate (VBR) MP3s bad, Constant Bitrate (CBR) MP3s good
  • HTTP 206 Header (Partial Content). If you are like me and got a soup nazi for web administrator, use curl or Fiddler and test your Apache because it might be turned off. We don't want a "No soup for you" incident.
Using SM2 and turning it into a Jquery plugin
SM2 can easily be used with Jquery and turn it into a plugin. The challenging parts are deciding on how to deal with the settings (and there are a lot of SM2 settings) and how to handle the sound objects.

For the setting, I decided to go with sensible defaults that can be overridden and use jquery's $.extend function to merge it. It more or less looks like:
// default settings
var settings = {
    autoplay: false,
    loop: false,
    playNextOnFinish: true,
    hideTrackDetailsAfterPlay: false, /* N (Number) seconds, or false (Boolean) to always show it */
    soundManagerMultiShot: false, /* let sounds "restart" or "chorus" when played multiple times..*/
    soundManagerStream: true,  /* allows playing before entire file has loaded (recommended) */
    soundManagerSwfURL: 'swf/', /* path (String), relative to your html page */
    soundManagerFlashVersion: 9,
    soundManagerDebug: false, /* displays the SM2 debug info into the page and in the console */
    soundManagerHandleFlashBlock: true,
    soundManagerPreferFlash: false,
    soundManagerHTML5Audio: true,
    soundManagerFlashLoadTimeout: 1000
};
    .
    .
    // jquery plugin
    $.fn.smsplayer = function(options){
      if(options){
         $.extend(settings, options); // Merge options to settings
      }
      return this.each(function (){
         ....
      }
    } 

Usage therefore would look like:


As for the Sound objects, I decided to to handle them in an array.

var tracks = [],trackIDs = [];
.
.
$("#player").find('#playlist li > a').each(function(i) {
  var soundID = 'sms_sound_' + i.toString();
      $(this).addClass(soundID);
      trackIDs.push(soundID);
      var sound = soundManager.createSound({
                  id: trackIDs[trackIDs.length - 1],
                  url: $(this).attr('href'),
                  whileplaying: function() {
                    Player.prototype.updateTime(this.position, this.durationEstimate);
                  },
                  whileloading: function() {
                    Player.prototype.updateLoading(this.bytesLoaded, this.bytesTotal);
                  },
                  onfinish: function(){
                    isPlaying = false;
                    $('#playlist .playing').removeClass('playing');
                        
                    if(settings.autoplay){
                       var nextSound = Player.prototype.getNextTrackFrom(trackIDs, this.id);
                       currentTrackID = nextSound;
                       $('.' + currentTrackID).parent().addClass('playing');
                       Player.prototype.playSound(nextSound);   
                    }
                         
                  }
       });
       tracks.push(sound);

I'm going to explain the Hotline Miami OST in part 2.

Sunday, September 2, 2012

Quick & Dirty Tabs on Drupal nodes

I just want a dead simple tab on a Drupal node but sometimes Drupal (with the internet) will just bend you over and spank you.

If you are reading this, I bet you found Quicktabs and read katbailey's "Go forth and tabbify" post but you came to the conclusion that its either:
  1. To complicated; You suspect that the hook_page_alter() function is gonna bite you in the ass down the line 
  2. Its not what you need 
I'll also bet you tried a few other approaches like leveraging the jQueryUI tabs like what Matt did and still found that it wasn't what you want. So now what? Well, you can do what I did - the quick and dirty tabs. 

My quick and dirty tabs is just javascript, html markup and a Omega subtheme. And it just involves 4 steps.

1. The HTML markup. You will use this inside your node.tpl.php or equivalent node template override. Notice the page-control and page-sections parts of the markup.
<div class="com-profile">  
      <div class="page-control">  
           <ul class="myQDtabs">  
                <li class="active"><a href="#">Basic Information</a></li>  
                <li><a href="#">Photos</a></li>  
                <li><a href="#">Forum</a></li>  
                <li><a href="#">Contact Persons</a></li>  
           </ul>  
      </div>  
      <div class="page-sections" style="display:none" >  
           <div class="section">  
                <div class="info">  
                     ...  
                </div>  
           </div>  
           <div class="section" style="display:none" >  
                <div class="gallery">  
                     ...  
                </div>  
           </div>  
           <div class="section" style="display:none" >  
                <div class="forum">  
                     ...  
                </div>  
           </div>  
           <div class="section" style="display:none" >  
                <div class="contactperson">  
                     ....  
                </div>  
           </div>  
      </div>  
 </div>  

2. The Stylesheet. I settled on a pill type of tabs. Just add these styles on the main subtheme stylesheet.
3. The Javascript. This is a tricky bit. If you have read Matt's post about Drupal and jQueryUI tabs, you'll know that Drupal allows other javascript frameworks so Drupal uses jQuery's .noConflict() function. This forces you to write your jQuery withouth that the '$' alias. Oh, create this script somewhere inside your subtheme.
jQuery(document).ready(function(){
    QDtabs('.myQDtabs','.page-sections','click');
});

//function-type tab switching
QDtabs = function($tabs,$tabcontent,method){
  jQuery($tabcontent + ' .section').eq(0).fadeIn();
  jQuery($tabs + ' li').eq(0).addClass('active');
 
  jQuery($tabs + ' li:not(.active)').die().live(method,function(e){
  e.stopPropagation();
  
  var $self = jQuery(this);
  var index = $self.index();  
  $self.addClass('active').siblings('.active').removeClass();  
  jQuery($tabcontent + ' .section').hide().eq(index).stop(false,true).fadeIn();
  
  return false;
 })
}

4. And the last step is making my Omega subtheme load the custom script. Omega has a particular way of adding custom javascripts.

libraries[my_script][name] = QD custom script
libraries[my_script][description] = Quick and dirty tabs
libraries[my_script][js][0][file] = my_scripts.js
libraries[my_script][js][0][options][weight] = 15

The last thing to do is a bit of housekeeping - Clear your Drupal cache and turning on my_script inside my theme's admin interface.


Saturday, January 28, 2012

That jquery click event and moving viewport problem

Sometimes certain combinations of attributes and values on a DOM element conspire to make you look stupid. In this case, a jQuery click function attached to an anchor tag with a href attribute.
<a href="#" class="reply-action">  
      <span>  
         <i></i>  
             Reply  
         </span>  
      </a>  
</span> 
And the jQuery is pretty straight-forward. The anchor tag when clicked will just toggle a div.replies to show or hide.
$('.reply-action').click(function () {
 $('.replies').toggle();
})
At this point, all things work. The replies div slide up and down as described by the jQuery toggle function. Normally this isn't a problem when you run this code at very top of the browser viewport. "Top of the browser viewport" simply means that the page hasn't scrolled down. The "moving viewport" problem happens when you run the click function when you're at bottom of the page. The click function keeps forcing the viewport to move to the top. Its annoying as hell. Fortunately the solution is simple. So simple in fact it made me go *FACE PALM*. The root of the problem is the the anchor href attribute set to "#". So by just changing the href attribute to something else like javascript:void(0) or just simply delete it, you fix the problem.
<a href="javascript:void(0)" class="reply-action">  
      <span>  
         <i></i>  
             Reply  
         </span>  
      </a>  
</span> 
It took me about a day to figure it out after trying to use overly complex jQuery like event and mouse APIs. Talk about being given the runaround.

Sunday, March 13, 2011

Using JQuery to read an external XML file

A student of mine asked me this question. He wanted to read an external XML file, parse it and load the data into a HTML Form control. In his case, he wanted to populate a select component (combo box). And he didn't want to use any PHP, Java or whatever. So that leaves me with JavaScript and HTML. This intrigued me for a bit. I haven't tried anything like this before.

So after a bit of research I stumbled into jQuery.ajax. I am familiar with jQuery - been using it for a bit so this should be quick. So, we prep the HTML file for our display:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Parsing Data from a File</title>

        <link rel="stylesheet" type="text/css" href="css/demo.css" />
        <script type="text/javascript" src="js/jquery-1.7.1.min.js"></script>
        <script type="text/javascript" src="js/demo.js"></script>

    </head>
    <body>
        <h2>Parsing Data from a File</h2>
        <p class="compress">Data will be from an external file. It will formatted as an XML file
            (this could be easily be a html file). It will be be parsed using JQuery library.
            We could do this with raw JavaScript but why would you want to do that when
            there is a simpler solution?</p>

        <p>To learn in detail how this thing works <a href="http://api.jquery.com/jQuery.ajax#options">read this.</a></p>

        <div id="form-div">
        Our Combo box:
            <form id="form1" class="cmbox">
                <select class="combo1">
                </select>
            </form>
        </div>       
    </body>
</html> 
And here is the demo.js file that comes with that file. If you run both now you'll get an error. This is because we have to create a file called data.xml. You should also notice that in the demo.js file (refer to the comments) loads the data.xml if it successfully loads the file it should call a function called "parse" and should it fail it calls the "loadfail" function.
$(document).ready(function(){       // load jQuery 1.5
 function loadfail(){
  alert("Error: Failed to read file!");
 }
 
 function parse(document){
  $(document).find("combo").each(function(){
     var optionLabel = $(this).find('text').text();
     var optionValue = $(this).find('value').text();
     $('.combo1').append(
    ''
     );
  });
 }
 
 $.ajax({
  url: 'js/data.xml',    // name of file with our data
  dataType: 'xml',    // type of file we will be reading
  success: parse,     // name of function to call when done reading file
  error: loadfail     // name of function to call when failed to read
 });
});
Here is the data.xml file.
<formdata>
    <combo>
        <value>1</value>
        <text>Option 1</text>
    </combo>
    <combo>
        <value>2</value>
        <text>Option 2</text>
    </combo>
    <combo>
        <value>3</value>
        <text>Option 3</text>
    </combo>
    <combo>
        <value>4</value>
        <text>Option 4</text>
    </combo>
    <combo>
        <value>5</value>
        <text>Option 5</text>
    </combo>
</formdata>

Do note that this will fail with Chrome because of its security model. If you look at the console it will not allow you to load local files because of Origin null is not allowed by Access-Control-Allow-Origin. Whatever that is.

Wednesday, March 3, 2010

Nip/Tuck (not the show)

I call this part Nip/Tuck (not the show) because now we are going to make this thing look good. It will be in two parts.

We are going to add some javascript to make it look good and use a CSS framework. When I say look good is we are going to add effects that so popular with Web2.0 websites like slide, fade and curl. For this to happen we are going to use JQuery (and probably JQueryUI) and maybe 960.gs for our CSS framework.

I know that Tapestry ships with Prototype but *whine on* I don't like prototype *whine off*. I know I might get flamed for this but guess what, my project, my stuff, go away.

Let's start with JQuery....

Now, it absolutely makes no sense in recreating the wheel on how to add JQuery into our Tapestry project. With that, we are going use somebody's else's wheel: ioko + maven.

Ioko-tapestry-commons' project, a series of GPL licensed components to assist in building tapestry websites and the easiest way to use them is to add them is by using maven (or other build systems) dependencies. Remember, we are only interested in the JQuery lib. So, power up your Netbeans6.8 and open up your pom.xml and we simply add ioko as a project dependency. Refer to the ioko-tapestry-commons website if you get lost, somehow.

Just look for the dependencies section of the pom.xml and add these 5 line. That's right five lines!

 <dependency>  
   <groupId>uk.co.ioko</groupId>  
   <artifactId>tapestry-jquery<artifactId>  
   <version>1.5.0-jquery-1.3.2</version>  
  </dependency>  

The module builds on the tapestry-javascript stack support and automatically includes jQuery in your pages. It automatically calls jQuery.noConflict() so be aware that you will need to write any jQuery code using the 'jQuery' or map it to another short hand form to avoid conflicts with Scriptaculous.

Run with it and have fun with JQuery.