Halifax Free Code Camp Meetup

October Challenge Discussion

A couple of people submitted solutions to the October challenge which was to create an alert when the user forgot to put his dropdown into ready mode. Graciously they have allowed me to post their solutions on this site.

The original description of the challenge is now on the Past Challenges page.

I also attempted the challenge. When talking to Ryan originally I suggested writing a Grease Monkey script. Little did I know that Grease Monkey no longer exists and has been forked to plug in called Tamper Monkey. One of our contributors used Tamper Monkey exactly like I would have done. But I decided to slug it out and go with the challenge I posed to others.

I will present my solution first and then show you what our other 2 submitters came up with.

First, a chrome extention requires a manifest file in json format


                    {
                        "manifest_version":2,
"name":"dropdown alert", "description":"set a 5 minute timer when not ready", "version":"1.0", "content_scripts":[ { "matches":["http://halifaxfcc.davidwatters.ca/*"], "js":["dropdown.js"], "run_at": "document_end" } ], "permissions":["tabs","http://halifaxfcc.davidwatters.ca/*"] }

Required elements of the manifest file are the version, name, the content_scripts section and the permissions that your script needs.Where your script will run is a recommended addition, I am unsure what the default is if you do not include it.

My only other file is a pure content script as indicated by my manifest dropdown.js


                    //find the dropdown
                    var check1=document.getElementById('queueStatus');
                    //create a looping timer that runs a function every 5 minutes
                    var myVar= setInterval(checkStatus,300000);			
                    function checkStatus (){
                    //send alert if you aren't in ready
                    if(check1.value != "Ready")
                        {
                            alert("You aren't in ready mode!");
                        }
                    }
                    

It is inelegant, but it it is quick and dirty and it works no matter how many tabs you have open in your browser.

It is vanilla javascript as well, if I wanted to, I could have used jquery, but I would have had added a link to the jquery source file in my manifest file. Lengieng one of our other submitters (whose solution is much more elegant and robust than mine) did just that.

Now I am going to show you Lengieng's solution. The full zipfile for his extension can be found here.

Lengieng's Manifest File:


                    {
                        "manifest_version": 2,
                        "name": "Status Checker",
                        "version": "0.1",
                        "content_scripts": [
                        {
                            "matches": [
                            ""
                            ],
                            "js": ["jquery-3.2.1.min.js", "content.js"]
                        }
                        ],
                        "background": {
                            "scripts": ["background.js"]
                        },
                        "browser_action": {
                            "default_icon": {
                            "19": "icon/icon19.png",
                            "38": "icon/icon38.png"
                            }
                        },
                        "icons": {
                        "16": "icon/icon16.png",
                        "48": "icon/icon48.png",
                        "128": "icon/icon128.png"
                        },
                        "permissions": ["notifications"]
                    }
                    

That's right, he even made it pretty with multiple icons in the appropriate sizes. He makes me feel so unprofessional!

Lengieng used 2 js files to pass messages which is recommended when writing chrome extensions and he had much greater knowledge of the chrome api than I did using the notification option rather than an alert which was just wonderful.

Lengieng's background.js File:


                    chrome.runtime.onMessage.addListener(
                    function(request, sender, sendResponse) {
                        if (request.action === "alert") {
                            var opt = {
                                type: "basic",
                                title: "Hey!",
                                message: request.message,
                                iconUrl: "icon/icon128.png",
                                requireInteraction: true
                        }
                    chrome.notifications.create('alarm', opt);
                    } else if (request.action === "clear") {
                        chrome.notifications.clear('alarm');
                    } else {
                        sendResponse({message: "unknown"});
                    }
                    });
                    

This is the file that constantly runs in the background listening for an alert from his content.js file. It will send an alert, clear an alert and it will send an unknown response to the content file for any other circumstance.

Lengieng's content.js File:


                    var $queueStatus = $("#queueStatus");

                    var alarm = {
                        timeout: 1000*60*3, // 3 minutes
                        repeat: true,
                        timeoutID: 0,
                        setup: function(timeout, repeat) {
                        this.timeout = typeof timeout !== 'undefined' ? timeout : 180000;
                        this.repeat = typeof repeat !== 'undefined' ? repeat : true;
                    },
                    start: function() {
                        if (this.timeoutID > 0) {
                        clearTimeout(this.timeoutID);
                    }

                    if (this.repeat) {
                        this.timeoutID = setInterval(this.warnUser, this.timeout, this.timeout);
                    } else {
                    this.timeoutID = setTimeout(this.warnUser, this.timeout, this.timeout);
                    }
                    },
                    stop: function() {
                    if (this.repeat) {
                    clearInterval(this.timeoutID);
                    } else {
                    clearTimeout(this.timeoutID);
                    }
                    this.timeoutID = 0;

                    // Clear the notification
                    chrome.runtime.sendMessage({ action: "clear" });
                    },
                    warnUser: function(timeout) {
                    var strTimeout;

                    if (timeout < 60000) {
                    strTimeout = (timeout / 1000) + ' second(s)';
                    } else {
                    strTimeout = (timeout / 60000) + ' minute(s)';
                    }
                    chrome.runtime.sendMessage({
                    action: "alert",
                    message: 'You are "' + $queueStatus.find("option:selected").text() +
                    '" for more than ' + strTimeout + '.'
                    });
                    }
                    };

                    if ($queueStatus.length > 0) {

                    // Setup the alarm clock
                    alarm.setup();

                    // React to dropdown change event
                    $queueStatus.on("change", function() {

                    // Stop the alarm if status is back to ready
                    if ($queueStatus.find("option:selected").text() === 'Ready') {
                    alarm.stop();
                    return;
                    }

                    // Start the timer and check the status
                    alarm.start();

                    });
                    }
                

Now, there are 2 things that I just love about this code. First is that I've never thought of formatting something like this as a json object using the different object properties as functions. It is genius and I am really surprised I haven't seen it more when learning javascript and jquery.

The second thing I love about it is that he resets his timer based on when the dropdown changes. I was going to do this, but decided to go with brute simplicity.

Finally, Brett submitted a Tamper Monkey script.

Brett's Tamper Monkey Script:


                // ==UserScript==
                // @name         Are you Ready Yet?
                // @namespace    http://tampermonkey.net/
                // @version      0.1
                // @description  Alert if ready not selected for 3min.
                // @author       Brett MacDonald
                // @match        http://halifaxfcc.davidwatters.ca/
                // @grant        none
                // ==/UserScript==

                (function() {
                    'use strict';

                    var e = document.getElementById("queueStatus");
                    e.addEventListener("change", readyYet);
                    var timerId;

                    function readyYet(event) {
                        if(e.selectedIndex === 0){
                            if (timerId){
                                clearTimeout(timerId);
                            }
                        } else {
                            if (timerId){
                                clearTimeout(timerId);
                            }
                            timerId = setTimeout(() => alert('Are you Ready Yet?'), 180000);
                        }
                    }
                })();
                

Like Lengieng, Brett based his timer on the change of the dropdown. Obviously both Lengieng and Brett don't have time for no 5 minute timers and went for 3 minutes :)

I'd like to truly thank both Brett and Lengieng so much for taking the time to submit and I really like seeing different people coming up with different ways of doing things. It helps you to realize there is always more than one way to approach a solution.