Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the acf domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /home/jwblogger/jwBlogger/wp-includes/functions.php on line 6121

Notice: Function wp_enqueue_script was called incorrectly. Scripts and styles should not be registered or enqueued until the wp_enqueue_scripts, admin_enqueue_scripts, or login_enqueue_scripts hooks. This notice was triggered by the codepen-embed-script handle. Please see Debugging in WordPress for more information. (This message was added in version 3.3.0.) in /home/jwblogger/jwBlogger/wp-includes/functions.php on line 6121
javascript – Jeff Wilkerson's Blog https://blog.jeffwilkerson.net The yoga of web development Tue, 03 Apr 2018 22:20:53 +0000 en-US hourly 1 https://wordpress.org/?v=6.8.5 Abstracting Youtube API Loading https://blog.jeffwilkerson.net/2018/04/03/abstracting-youtube-api-loading/ https://blog.jeffwilkerson.net/2018/04/03/abstracting-youtube-api-loading/#respond Tue, 03 Apr 2018 22:20:53 +0000 http://blog.jeffwilkerson.net/?p=333 I’ve been on several projects when I was asked to display Youtube videos on a webpage. Well, unless there is only one video on the page, and that video is loaded statically, you might need to leverage the Youtube API.

Every time I’ve placed a Youtube video on a page, its never as easy as slapping an IFRAME on the page. Sometimes the video loads dynamically, like when opening a modal window. Sometimes there is more than one video on the page, and you need to control the videos via javascript so that only one video plays at a time. Sometimes the web page being loaded needs to be as quick as possible, and therefore need to initialize videos on demand. To say to least, I almost always make use of the Youtube API to display videos on web pages.

I’ve posted a helper module to my Github account that abstracts the details of loading the API. The module encapsulates the code needed to load the API library asynchronously and also defines the required `onYouTubeIframeAPIReady` function that is called once the API is loaded and ready. The function fires an event from the body to notify other modules that the API is ready. Once the API is ready, modules can initialize players.

Default Usage

	
     var ytHelper = new YoutubeHelper();
     ytHelper.init(function() {    	

     	// DO SOMETHING ONCE API IS READY

     });
]]>
https://blog.jeffwilkerson.net/2018/04/03/abstracting-youtube-api-loading/feed/ 0
Adding Items to List with KnockoutJS https://blog.jeffwilkerson.net/2017/05/08/adding-items-to-list-knockoutjs/ https://blog.jeffwilkerson.net/2017/05/08/adding-items-to-list-knockoutjs/#respond Mon, 08 May 2017 15:02:04 +0000 http://blog.jeffwilkerson.net/?p=307 My last blog post presented a KnockoutJS component that toggled text into a form field. This code sample is ripped from a project I’ve been working on lately. To give context, this project is a small web application that allows administrators to edit details of a group of users.

Today I will show another component from that same project that will allow the administrator to assign items to a user using a dropdown.

Check out the Codepen

In this particular example, the items are baseball teams, and we are assigning “favorite teams” to the user. To assign a team to the user, the administrator selects the team from the dropdown list, then clicks a button to assign the team to the user. Once the administrator adds 5 teams, the dropdown is disabled, and a message is displayed to the user.

The list used to populate the dropdown is an array of objects. Each object presumably will have ID and Name attributes.

The View

Let’s first take a look at my view (my HTML) of this component:

<add-to-list params="list: $root.filteredItems(), optionsText: 'name', submit: addItem, disable: (selectedUser().List().length >= 5), limitMsg: 'Stop it! you have enough favorite teams.'">
</add-to-list>

The component is a custom html element called `add-to-list`. This element, like all KnockoutJS components, has a params attribute. I use this attribute to pass data from my ViewModel to my component. The data I’m passing is:

list
The list used to populate the dropdowns.
optionsText
This mimicks the optionsText binding of KnockoutJS, and tells the component which property of the object to display in the dropdown.*
submit
Function to add the item to the User’s list
disable
Boolean expression that tells component when to disable itself.
limitMsg
Text to display when component disables itself.

*Yes, I could implement a ‘optionsValue’ binding. However, for demonstration purposes, I’m going to assume the value of each item in the dropdown list will be the ID property of each item.

Filtering the Master List

In this example, I am giving the component a function that returns a list rather than the list itself. This is so I can take out any items that the user already selected. Here is the function for that.

/* function to filter the main data list depending on what the user has selected */
	self.filteredItems = function() {
		return ko.utils.arrayFilter(self.allDataList(), function(listItem) {
			var match = -1;
			ko.utils.arrayForEach(self.selectedUser().List(), function(item) {
				if(item.ID == listItem.ID)
					match = 1;
			
			});
			return match <= 0;
		});
	};

Below is the codepen that demonstrates the complete component. My next blog post will show the complete admin screen that will contain this component combined with the edit-inline component from a previous blog post.

See the Pen Add-to-list KnockoutJS component by Jeff Wilkerson (@stljeff1) on CodePen.

]]>
https://blog.jeffwilkerson.net/2017/05/08/adding-items-to-list-knockoutjs/feed/ 0
Playing with KnockoutJS: Custom Bindings and Components https://blog.jeffwilkerson.net/2017/05/03/playing-with-knockoutjs-custom-bindings-and-components/ https://blog.jeffwilkerson.net/2017/05/03/playing-with-knockoutjs-custom-bindings-and-components/#respond Wed, 03 May 2017 20:14:09 +0000 http://blog.jeffwilkerson.net/?p=297 I love KnockoutJS! It may dirty up my HTML, but I can make things fast!

In a recent project, I was working with a listing of people, and clicking on one person displayed the details of that person to the right of the listing so that the user could edit the details of the selected person. Within the form, the client wanted to show thetext fields as clickable text – clicking the text would toggle the text into an input field. Blurring the input field would toggle the element back to text.

The page already had KnockoutJS and Bootstrap available, so I decided to leverage those tools. I ended up writing a custom component to solve the problem. Here is the codepen showing my solution.

See the Pen Edit-Inline with Knockout by Jeff Wilkerson (@stljeff1) on CodePen.

Included in this example is a custom binding that saves the value ONLY IF the user hits the ‘Enter’ key. This binding is not mine; it was inspired by a StackOverflow post (maybe this one?) or maybe by Ryan Niemeyer .

Check out the full code on GitHub, or see the codepen.

]]>
https://blog.jeffwilkerson.net/2017/05/03/playing-with-knockoutjs-custom-bindings-and-components/feed/ 0
Unbirthdays! Playing with Kendo UI DatePicker https://blog.jeffwilkerson.net/2017/04/20/unbirthdays-playing-with-kendo-ui-datepicker/ https://blog.jeffwilkerson.net/2017/04/20/unbirthdays-playing-with-kendo-ui-datepicker/#respond Thu, 20 Apr 2017 21:41:42 +0000 http://blog.jeffwilkerson.net/?p=316 I’ve been playing with a massive Kendo UI Grid for the past few months. I really enjoy Kendo UI – I can build functionality quickly, and the forums are very active and helpful. But there is so much grid I can take!!

I decided to take a break from my grid for a few days and familiarize myself with the Kendo UI Datepicker. I gave myself a project to calculate the number of unbirthdays someone has had. So many people tell their age in the number of birthdays. Well, wouldn’t it be crazy if we all spoke our age in terms of unbirthdays? (These all the thoughts one has when they write lots of javascript and suffer from a lack of sunlight.)

Plus, today is the 34th anniversary of my first unbirthday!

See the Unbirthday Calculator at the bottom of this blog post.

Quick Takeaways

Despite the absurdity of the purpose of this project, these are my takeaways from this exercise:

  • Initialize a Kendo UI DatePicker
  • Apply a validator to a form field
  • A renewed, deepened understanding of the Date Object

The Code

Check out the code on Github.

Calculating Unbirthdays

To calculate unbirthdays, you first calculate the number of days in between today and your birth date, then subtract the number of birthdays. Here is what that looks like in javascript:

var calcUnBirthday = function(bday, today) {

	//get difference between two dates, in milliseconds
	var diff = today.getTime() - bday.getTime();

	//find age by converting difference into Date, then subtracting from 1970.
	//ref: http://stackoverflow.com/questions/4060004/calculate-age-in-javascript
	var diffDate = new Date(diff);
	var age = Math.abs(diffDate.getUTCFullYear() - 1970);
	
	//calculate days since birthdate by converting the difference into days.
	var daysSince = Math.ceil(diff / (1000 * 60 * 60 * 24));

	//calculate unbirthdays
	var unBirthdays = daysSince - age;

	return unBirthdays;
};

Working with the Kendo UI DatePicker

As with all the Kendo UI Widgets, there is ample examples and documentation provided by the Kendo UI Team.

One thing that stumped me is how to allow the user to type in a date, validate the input, then trigger the change event of the DatePicker. I was confused as to why I couldn’t trigger the change event of the DatePicker by typing in a value. This led me to implement a Validator, as well as a ‘keyup’ event handler for when the user presses ‘Enter’ and submits their input. By wrapping my DatePicker in a Validator, and adding a ‘keyup’ event handler, I am able to allow the user to type in a value if they choose to.

Take a look at all the code to see for yourself what I’m talking about!

The Result

Without further ado, here is the unbirthday calculator.

Special Thanks to…

@Event_Hoorizon for creating this simple codepen showing how to create confetti.

]]>
https://blog.jeffwilkerson.net/2017/04/20/unbirthdays-playing-with-kendo-ui-datepicker/feed/ 0
Idle Timeout w/ Angular JS https://blog.jeffwilkerson.net/2016/11/18/idle-timeout-angularjs/ https://blog.jeffwilkerson.net/2016/11/18/idle-timeout-angularjs/#respond Sat, 19 Nov 2016 00:17:40 +0000 http://blog.jeffwilkerson.net/?p=291 A client was concerned about keeping users on the site, and wanted a way to “shout” a call-to-action be if the user was about to leave the site. The call-to-action could be some text, or a video, or a form, the client wasn’t sure what would work.

I see this kind of thing on blogs all the time, and wondered to do it. Particularly, how do I anticipate the user leaving the site? Browsers prohibit using modals and tooltips when listening for document.unload events. I could listen for when the user loses focus on the browser window (ie. $('body').on('mouseout'...)) but that wouldn’t work on phones.

How do I gracefully and respectfully grab the attention of the user without impacting on usability?

As with any solutions, there are user-friendly solutions, and solutions that are intrusive. The solutions that I liked involved a modal showing, fading out the rest of the site, and easily dismisible. It grabs my attention, but simply goes away and shuts up if I don’t want to engage.

It was an interesting problem. At the very least, the problem was a useful challenge as I continue to grow my Angular JS skills. How do I gracefully and respectfully grab the attention of the user without impacting on usability?

While I waited for the client to figure out what he wanted, I took time for myself to explore how I could use AngularJS to solve the problem swirling in my head. I decided I wanted an object that created a mechanism that listened to the user’s behavior, and fired an event if the user goes idle. Once the user goes idle, I would listen for the user to make another move and use that event to fire my call to action.

The Solution

Inspired by this post on StackOverflow, I created a Factory object that listened for idle behavior. I knew I needed two pieces of functionality. One piece would manage a timeout with the ability to create a timeout, and fire an event when the timeout ended. Also needed was the ability to “interrupt” the timeout by canceling the existing timeout and starting a new timeout on every user action. The second piece needed was the listening for the user interaction, forwarding those events to the timeout object, and to stop listening if the timeout ended.

See the Pen Idle Timeout with AngularJS by Jeff Wilkerson (@stljeff1) on CodePen.

Some other examples solving similar problems that I saw used $rootScope and variable watches. I understand that approach conceptually, however I am still confused on the whole Digest Cycle thing in Angular JS, and for this little exercise, I wanted to avoid using $rootScope to propagate events.

]]>
https://blog.jeffwilkerson.net/2016/11/18/idle-timeout-angularjs/feed/ 0
AngularJS Module using Youtube API https://blog.jeffwilkerson.net/2016/01/11/angularjs-module-using-youtube-api/ https://blog.jeffwilkerson.net/2016/01/11/angularjs-module-using-youtube-api/#respond Mon, 11 Jan 2016 23:08:35 +0000 http://www.jeffwilkerson.net/?p=201 After about 6 months of casually dabbing in AngularJS, I have learned enough to start producing useful, production-ready modules. It has been an awkward learning curve to say the least, but I have kept with it, and now would like to document a module I have created for a client. I have replaced the Youtube links used for the client with videos from The Tonight Show.

The Objective

The client has buttons linking to Youtube videos throughout the site. Each link was going directly to the external Youtube page. The client wanted to keep the buttons, but have the videos launch a modal window taking up as much screen space as possible. Some pages had one link, other pages had many links. The site is using AngularJS and Bootstrap.

The Process

First, as with many projects, I surveyed Google to see if anyone had created what I needed. I found many tutorials showing how to create modals (see Ben Nadel). I also found this widget that uses the Youtube Developer API inside an AngularJS Directive, but no examples showing how to use the Youtube Developer API to load a video in a modal window. Nonetheless, I found Ben Nadel’s blog and Fran Dios’s jukebox app valuable as I continue to learn how to organize my AngularJS moduler.

The Results

View the full demo here.

This project taught me about module.run(), and how to compile templates inside directives.

]]>
https://blog.jeffwilkerson.net/2016/01/11/angularjs-module-using-youtube-api/feed/ 0
Google Maps Demo https://blog.jeffwilkerson.net/2015/04/17/google-maps-demo/ https://blog.jeffwilkerson.net/2015/04/17/google-maps-demo/#respond Fri, 17 Apr 2015 18:34:57 +0000 http://www.jeffwilkerson.net/?p=161 Below is the process and code that I used to create this map showing Instagram pictures from a road trip I took starting in Nashville, TN and ending in Portland, OR. This exercise demonstrates:

  • API Development
  • Consumption of JSON Data
  • Object-Oriented Principles

Live Demo Page

The Process

  1. First, I gathered the Instagram pictures I wanted to show on my map. Because I didn’t properly tagged the original Instagram posts with any location info, or anything that would distinguish the posts from my other posts, I had to build my own custom results set using the results set from Instagram as a foundation. I employed Instafeed.js to help build my results set. Here is the final JSON object.
  2. Next, I got my App Key from Google and built a “Hello, World!” test to make sure everything worked.
  3. Finally, I created two javascript classes, MyList and MyMap. MyList is a glorified array of Map Markers, with an internal pointer that tracks the “current” index. MyMaps is a wrapper class for the google.maps.Map object, adding UI controls.

The Code

Class: MyList

/***************** CLASS: MyList **********************/
/* * 
    Stores a list of pbjects, and a a reference pointing to a selected list item
    Functions:
        add - Adds item to the list
        saveAll - Adds an array of items to the list
        count - returns number of items in list
        getAll - returns entire list
        get - gets the item at a specified index
        indexOf - returns index number of specified object, making that object the new selection
        next - returns the next object in the list
        previous - returns the previous object in the list
        triggerMarkerIcon - toggles marker icon of the selected index;
            Parameter toggle - boolean. False for default red flag; true for green flag
    */
function MyList() {
    this.list = [];
    this.pointer = -1;	
}

MyList.prototype.add = function(obj) { return this.list.push(obj); };
MyList.prototype.saveAll = function(array) { if(array.constructor === Array) this.list = array; };
MyList.prototype.count = function() { return this.list.length; };
MyList.prototype.getAll = function() { return this.list; };
MyList.prototype.get = function(i) {
    if(i > -1 && i < this.list.length) {
        this.triggerMarkerIcon();
        this.pointer = i;
        this.triggerMarkerIcon(true);			
        return this.list[i];
    }
};
MyList.prototype.indexOf = function(marker) {
    this.triggerMarkerIcon();
    for(var i = 0; i < this.list.length; i++) {
        if(this.list[i] === marker) {
            this.pointer = i;
            this.triggerMarkerIcon(true);
            return i;
        }
    }
    return -1;
};
MyList.prototype.next = function() {
    this.triggerMarkerIcon();
    this.pointer++;
    if(this.pointer == this.list.length) this.pointer = 0;
    this.triggerMarkerIcon(true);
        
    return this.list[this.pointer];
};
MyList.prototype.previous = function() {
    this.triggerMarkerIcon();
    this.pointer--;
    if(this.pointer < 0) this.pointer = this.list.length - 1;
    this.triggerMarkerIcon(true);
        
    return this.list[this.pointer];
};
MyList.prototype.triggerMarkerIcon = function(toggle) {
    var icon = red_icon;
    if(toggle) icon = green_icon;
    
    if(this.pointer > -1 && this.list[this.pointer]) {
        this.list[this.pointer].setIcon(icon);
    }
};

Class: MyMaps

/***************** CLASS: MyMap **********************/
/* * 
    Wrapper class for Google Maps Object. 
    customized to show a list of Instagram images, each marked on a map
    Functions:
        changeMarker: activates the marker chosen by the user. Displays infoWindow
        addMarker: creates a custom marker object, including the content for the marker, 
            extrracted from the data object.
            Expands the bounds of the map.
        initControls: Adds the 'Next' and 'Previous' click event handlers allowing the user to go back
            and forth between markers
        initMap: initializes Map, stores the markers in a list
    */
function MyMap() {
    this.map = undefined;
    this.el = undefined;
    this.infoWindow = new google.maps.InfoWindow();
    this.bounds = new google.maps.LatLngBounds();
    this.markers = new MyList();
}

MyMap.prototype.changeMarker = function(marker, origin) {

    if(!origin)
        this.markers.indexOf(marker);

    this.infoWindow.setContent(marker.content);
    this.infoWindow.open(this.map, marker);	
};

MyMap.prototype.addMarker = function(mark) {
    var self = this, date, date_string, latlng, gMarker;
    if (mark.location.lat != undefined && mark.location.long != undefined) {
        
        date = new Date(mark.created_time * 1000);
        date_string = monthnames[date.getMonth()] + ' ' + date.getDate();
                    
        latlng = new google.maps.LatLng(mark.location.lat, mark.location.long);
        
        gMarker = new google.maps.Marker({
            position: latlng,
            map: self.map,
            title: date_string
        });
        
        gMarker.content = "
"+date_string+"

"+mark.caption.text + "
Filter: " + mark.filter + "
"; google.maps.event.addListener(gMarker, 'click', function() { self.changeMarker(gMarker); }); this.markers.add(gMarker); this.bounds.extend(latlng); } } MyMap.prototype.initControls = function() { var self = this; $('.next-marker').click(function(e) { e.preventDefault(); self.changeMarker(self.markers.next(), this); }); $('.previous-marker').click(function(e) { e.preventDefault(); self.changeMarker(self.markers.previous(), this); }); }; MyMap.prototype.initMap = function(id, data) { this.el = $(id)[0]; this.map = new google.maps.Map(this.el, {zoom: 8}); var self = this; for(var i = 0; i < data.length; i++) { this.addMarker(data[i]); } this.map.fitBounds(self.bounds); this.changeMarker(this.markers.next()); };

$(document).ready()

map_to_pdx = new MyMap();
	
$(function() {
    
    if(!pdx_trip_data_uri) {
        console.log("ERROR. No input.");
    }
    else {
    	//#map-container is hidden on small screens
        if($('#map-container').is(':visible')) {
            //Get Data from JSON Object
            var jqxhr = $.getJSON(pdx_trip_data_uri.path);
            
            jqxhr.done(function(data) {
                if(data.length > 0) {
                    map_to_pdx.initMap('#map-canvas', data);
                    map_to_pdx.initControls();
                }
            });
        }
    }
});

Links

Special Thanks To:

]]>
https://blog.jeffwilkerson.net/2015/04/17/google-maps-demo/feed/ 0
Advanced DOM Manipulations https://blog.jeffwilkerson.net/2015/03/14/advanced-dom-manipulations/ https://blog.jeffwilkerson.net/2015/03/14/advanced-dom-manipulations/#respond Sat, 14 Mar 2015 19:44:58 +0000 http://www.jeffwilkerson.net/?p=177 In a previous job, while working with a CMS in its infant stage, I often used jQuery to add a custom user experience to quickly satisfy a customer while giving time to the Scrum so that those features could be built in future sprints.

In one instance, a customer wanted one credit application for both individual and joint applicants. If the user needed to fill out a joint application, the client wanted to use the same form, as oppose to submitting two individual form. Furthermore, the client needed the same required field on the Individual form to be the same as the Joint Applicant form.

Review the final result.

Review the code on jsFiddle

Operating in an enterprise-level Content Management System, I needed to create the form with ALL the fields present on page load; I couldn’t just add the fields for the Joint application via javascript after page load. I also needed a way to toggle the required fields of the Joint application – if the user submitted an Individual app, the required fields for the Joint applicant need not to be required, and vice versa. Finally, I needed to make it look good and sensible to the end user.

The Process

As mentioned, I first had to create the form from inside a CMS. I had the ability to only specify a form field’s name and its ‘required’ state. I could not add classes to the form items. Furthermore, I had no ability to create form sections! Forms were basically delivered in one large UL element.

My first step was to separate the Joint Applicant fields from the Individual fields. When I created the form in the CMS, I made sure all the Joint Application fields were grouped together (prepending all Joint Application fields with ‘Co-Applicant’), and same with the fields for the Individual fields. I created two objects to act as pointers to let me keep track of the two group of fields within the DOM.

var primary = {
    start: 0,
    end: $('label:contains(Co-Applicant):first').parent().prev().index('#JointApp form > ul > li'),
    name: "Primary_Form_Section"
}

var coapp = { start: primary.end,
        end: $('label:contains(Co-Applicant):last').parent().index('#JointApp form > ul > li'),
        name: "Coapp_Form_Section"
}

Next, I built new containers for the Individual and Joint Applicant fields, and began placing the necessary fields in those containers.

    primary.items = 
    $('#JointApp form > ul > li').filter(function(i) { return filterEls(i, primary.start, primary.end); });
    
    coapp.items = $('#JointApp form > ul > li').filter(function(i) { return filterEls(i, coapp.start, coapp.end); })
    
    coapp.items.each(function(i, el) {
        var label = $(el).children('label');
        label.html(label.html().replace('Co-Applicant ', ''));
    });
    
    primary.items.appendTo('#'+primary.name+'-Tab').wrapAll('
    '); coapp.items.appendTo('#'+coapp.name+'-Tab').wrapAll('
      ');

Finally, I created a Tabbed interface so the user could easily toggle back and forth between Joint and Individual applications.

    	
	// Function used to make tabs for new tab panel
	function customTab(text, target, isActive) {
	   
		return $(''+text+'').addClass('customTab ' + (isActive ? "active_customTab":"")).click(function(e) {
			e.preventDefault();
			var id = this.rel;
		   
			$('.active_customTab').removeClass('active_customTab');
		   
			$('.customTabContent').hide();
		   
			$(this).addClass('active_customTab');
		   
			$('#'+id).show();
		   
		});
	}
    
    // Menu for custom Tab Pane;
    $('
').append( $('
    ').append( $('
  • ').append(customTab('Primary Applicant', primary.name, true))) .append( $('
  • ').append(customTab('Co-Applicant', coapp.name)) )).prependTo('#CustomTabs'); $('#customTabMenu').after('
    '); primary.items.last().after( $('
  • Continue to Co-Applicant Part
  • ') .click(function(e) { $('html, body').animate({ scrollTop: $("#CustomTabs").offset().top }, 300); $('#'+coapp.name).trigger('click'); } ).hide() ); // Activate first tab of tab panel $('a.customTab:first').addClass('active_customTab');

    That’s the basics of the project. If you have read this far, you should really check out the code on jsFiddle and Review the final result.

    ]]>
    https://blog.jeffwilkerson.net/2015/03/14/advanced-dom-manipulations/feed/ 0