Archive for the ‘Javascript’ Category

Download the code.

Demo.

Download the WP Audio Player Standalone.

So the rabbi asked me to add the ability to play the audio on the taamim page (basically, a long list of short MP3's) directly on the page, rather than click the link to open a new page. No problem, right? We're living in an HTML5 world, so I should be able to do:

$('a[href$=mp3]').each(function(){
  $('<audio>').attr({src: this.href, controls: 'controls'}).insertBefore(this);
});

And everything ought to work: browsers that can't handle <audio> elements get nothing, modern browsers get a modern audio player. Nice progressive enhancement.

But of course, it's not that easy. Webkit (Chrome, Safari) supports MP3 playing, but Firefox does not (and won't), and Internet Explorer only does for IE9 and up, and I have to support Windows XP and IE8 (source; consistent with my experimentation). I don't like the <embed>ed players, so I'll go with Flash. I like the player that Language Log uses, and viewing the source tells me that's WPAudioplayer, which has a standalone version that requires just two files, the 11-kb swf file and a 12-kb javascript file.

To use it, include the javascript with a <script> element and initialize the player with AudioPlayer.setup('/path/to/player.swf', {width: 100}); where 100 is the desired width of the player in pixels (it's constant for every player on the page and it's a mandatory option). Then, each player is implemented by replacing an existing element, identified by id: AudioPlayer.embed(id, {soundFile: '/path/to/soundfile.mp3'});.

Of course, iOS won't run Flash, so I still need to use the <audio> element there. So I need to detect if the audio element works, and if not, insert the Flash substitute. Browsers that can't handle either get a blank spot.

Putting it together into a plugin:

(function($) {

var uid = 0;
var init = function (swf, width){
	AudioPlayer.setup(swf, {width: width});
	init = $.noop;
}
$.fn.inline_mp3 = function(swf){
  return this.each(function(){
		var id = 'audioplayer_'+(uid++);
		var player = $('<audio>').attr({
			src: this.href,
			controls: 'controls',
			id: id
		}).insertBefore(this);
		// audio.canPlayType test from http://diveintohtml5.com/everything.html#audio-mp3
		if (!(player[0].canPlayType && player[0].canPlayType('audio/mpeg;').replace(/no/, ''))){
			init (swf, player.width());
			AudioPlayer.embed(id, {soundFile: this.href});
		}
	});
};
})(jQuery);

It uses a unique number to assign an id to each element, and lazy-initializes the Flash player. The player should be styled with a given width (since IE8 doesn't have a default <audio> size):

audio {
	width: 80px; 
	display: inline-block;
}

And use it:

$('a[href$=mp3]').inline_mp3('/path/to/player.swf');

And there are lots of other packages of html5/Flash fallback audio players but this is small and easy enough for me to understand.

Modern javascript provides a host of cool functional-programming toys like forEach and reduce, but I'm stuck with IE* at work, which does not. jQuery fills some of those gaps (it has $.each and $.map) but not everything one would want.

Enter underscore.js, which bills itself as "the tie to go along with jQuery's tux."

It doesn't modify any native objects, requiring you to wrap them as _([1,2,3]).map(function(x) {return 2*x;}), which returns [2,4,6]. Chaining has to be explicitly enabled, _([1,2,3]).chain().map(function(x) {return 2*x;}).reduce(function(memo, num){ return memo + num; }, 0) returns 12.

Looks useful.

I put yesterday's unmask password plugin together with the code to automatically create a checkbox to control it, and removed the dependency on jQuery UI.

Download the code.

Example

<input type="password" id="password"/>
$('#password').showPassword('Show Characters as I Type');

Use

$('[type=password]').showPassword(string) creates a checkbox as above with string as the label. If the checkbox is checked then the password field is unmasked. $('type=password').showPassword(true) unmasks the field directly, and $('type=password').showPassword(false) restores masking.

I hate password masking—hiding what I'm typing to prevent someone from looking over my shoulder at my passwords. I know when I'm in public and when I'm alone in the house, and it should be my choice to actually see what I'm typing, since it has to have some nonsense combination of characters. Even Jakob Nielsen agrees with me.

The logical thing to do is to have a checkbox that would show the characters, and the easiest way to do that is to change the type of the input element from password to text.

For most browsers, this is nontrivial but doable. You have to detach the element first in order to change its type. But in Internet Explorer, there's no way to change it. One solution is to create a new input element with type text and copy all the other attributes into it, but that seems like overkill. All we need is a way for the user to see what he is typing, so a tooltip-like overlay would work.

This is what I came up with (note that it requires jQuery UI position:

<input type="password" id="password"/>
<label><input type="checkbox" id="check"/> Show characters as I type</label>
try{
  $('<input type="password">').detach().attr('type', 'text');
  $.fn.showPassword = function(shouldShow){
    var type =  shouldShow ? 'text' : 'password';
    return this.each(function(){
      /* from http://stackoverflow.com/questions/540851/jquery-change-element-type-from-hidden-to-input */
      marker = $('<span />').insertBefore(this);
      $(this).detach().attr('type', type).insertAfter(marker);
      marker.remove(); 
    });
  };
}catch(e){
  $.fn.showPassword = function(shouldShow){
    return this.each(function(){
      if (shouldShow){
        var span = $(this).siblings('.showPassword');
        if (span.length > 0){
          span.show();
        }else{
          span = $('<span>').addClass('showPassword').css({
            opacity: 0.9,
            background: 'white',
            position: 'absolute',
            fontFamily: 'sans-serif',
            paddingLeft: '2px'
          }).position({
            at: 'left bottom',
            of: this,
            my: 'left top'
          }).text(this.value).insertBefore(this);
          $(this).keyup(function(){ span.text(this.value); });
        }
      }else{
        $(this).siblings('.showPassword').hide();
      }
    });
  };
}
$('#check').change(function(){
  $('#password').showPassword($(this).is(':checked'));
});

Try this in Internet Explorer as well as a standards browser. The details are different, but the functionality is the same.

tl;dr

Download the code. Use $.validateHTML5(callback), where callback (result, data) is a function that is called with the result, a string that can be "valid" if the page validated, "invalid" if it did not, "warning" if it validated but is "in some way questionable", or "not validated" if some error occurred. data is the actual JSON output from the validator.

Continue reading ‘HTML5 validator plugin’ »

Updated flexcal to 1.2.1; nothing major, just changed the Hebrew numbers to use the technically correct Unicode code points HEBREW PUNCTUATION GERESH (&#x05F3; ׳) and HEBREW PUNCTUATION GERSHAYIM (&#x05F4; ״) rather than single and double quotes. Also similarly updated the Hebrew keyboard.

This post is obsolete; since moving the code to github, the package is no longer being maintained. You need four files: flexcal.css, jquery.ui.subclass.js, jquery.textpopup.js and jquery.flexcal.js

There's been some interest in putting flexcal and all its dependencies into a single file (that would be jquery.ui.subclass.js, jquery.textpopup.js, jquery.flexcal.js, and flexcal.html). The problem with putting it all into a ZIP file is keeping it updated; I don't have an automated make-like system and there's no way I'm going to remember to keep the package up to date. So I created a PHP script based on Patrick Hunlock's Supercharged Javascript to pull the javascript together automatically, together with a script to create the HTML template (so no AJAX is needed and no flexcal.html).

Download the code.

I've had some questions about extending flexcal so I created one that combines filtering, output formatting and drop-down menus.

The filtering will only allow dates going back 17 years, and the calendar will start on that date. The formatting will use European dates (d/m/y). The drop-down menus will be the ones from the original post, but instead of using aspect-oriented programming (flexcal('after', '_adjustHTML', function (cal){...) we will subclass the original widget.

Continue reading ‘A flexcal Example’ »

In a major burst of middle-of-the-night hacking, I've updated flexcal to better handle changing options after instantiation and added a current option, representing the currently highlighted date. I also updated textpopup with a new option, box that allows you to set the popup's container, useful for inline widgets. The flexcal API examples use it for an inline datepicker.

My flexcal plugin exposes a few useful methods, which I have not documented elsewhere. They are called, like all jQuery UI widget methods, by creating the widget: cal = $('input.date').flexcal() then invoking the method: cal.flexcal('setDate', '10/25/2011').

Continue reading ‘The flexcal API and an inline flexcal’ »