Javascript, Web Development

Extending Select2 with Adapters

Original Post: https://bojanv91.github.io/posts/2017/10/extending-select2-with-adapters-and-decorators – Extending Select2 with Adapters: Bojan’s Notes.

I have ripped this post to avoid dead-links.

Starting from version 4.0, the Select2 jQuery plugin uses the adapter pattern as a way for developers to extend its features and behavior. Having implemented very custom select-based components, I can assure you that this is very powerful and useful feature.

Unfortunately, the docs about this feature do not include concrete usage examples, so it’s quite difficult to get started, to understand how to use it, and how to build on top of it – especially if you haven’t worked with jQuery plugins for a while. So, this article offers usage examples and describes how to use Select2 adapters and decorators feature.
What are adapters and decorators in Select2?
Adapter, adapts interface of an existing class to another interface. For example replacing the appearance of the selection input element with icon element.

Decorator, attach additional responsibilities to an object dynamically. For example adding functional checkboxes in multiple select dropdown items, or dedicated search field in multiple selection dropdown.

Select2 has several built-in adapters that can be used, overridden, and modified. You can find them explained in the advanced section in the docs, and their source code in the GitHub repository. For example, here is how SelectionAdapter is implemented (link to source file).

From the internal use of the adapters in select2 source code you can actually re-use and adapt their code in your apps. This is the approach that helped me to better understand how to use this feature.

You can take total control of the appearance and behavior of select2-based elements in your web apps by learning and exposing the full potential of this feature.

Defining and using custom adapters
A Select2 adapter is defined using an AMD module. Adapters can use other adapters or objects (to extend or decorate) by taking AMD module dependencies. Select2 automatically loads modules when the adapters are being constructed.

$.fn.select2.amd.define(“CustomSelectionAdapter”,
[
“select2/utils”,
“select2/selection/multiple”,
“select2/selection/placeholder”,
“select2/selection/eventRelay”,
“select2/selection/single”,
],
function (Utils, MultipleSelection, Placeholder, EventRelay, SingleSelection) {
// Here goes the code of this custom adapter
}
);
This piece of code defines a custom SelectionAdapter. It should be executed only once. Usually it is called from application start, just after external libraries (like jQuery, Select2) are loaded, or just before it’s first usage, in a lazy-execution way. For single-page applications, the lazy-execution is preferred approach.

Custom adapters can be used when constructing select2 elements, by requiring the AMD module in which they are defined to the select2 configuration API. Bellow is an example where the already defined custom selection adapter is used.

$(htmlElement).select2({
data: sampleDataAsArray,
selectionAdapter: $.fn.select2.amd.require(“CustomSelectionAdapter”)
});
Now, the htmlElement will be transformed to a select2 element with customized selection interface and behavior, as defined in CustomSelectionAdapter. Besides customizing the selectionAdapter, you can customize also the resultsAdapter, dataAdapter, ajaxAdapter, resultsAdapter, and dropdownAdapter.

Example: Custom multiple select
This example demonstrates the full power of adapters and decorators feature. It extendsSelectionAdapter and DropdownAdapter to fulfil the goals.

Default behavior of the multiple select, to be modified is:

Ability to search for items directly from the selection-box.
Show selected items in the selection-box.
Desired new behavior:

Ability to search items from a dedicated search box, shown in the dropdown (same as in single select).
Show number of selected items out of the total in the selection-box. Also, show arrow icon in the selection-box.
At image bellow, the default and desired outcomes are visualized.

Custom multiple select

To make select2 do the desired behavior, two custom adapters need to be written, a SelectionAdapter and a DropdownAdapter.

Find the complete solution example on jsFiddle.
Step 1 – create custom selection adapter

$.fn.select2.amd.define(“CustomSelectionAdapter”, [
“select2/utils”,
“select2/selection/multiple”,
“select2/selection/placeholder”,
“select2/selection/eventRelay”,
“select2/selection/single”,
],
function(Utils, MultipleSelection, Placeholder, EventRelay, SingleSelection) {

// Decorates MultipleSelection with Placeholder
let adapter = Utils.Decorate(MultipleSelection, Placeholder);
// Decorates adapter with EventRelay – ensures events will continue to fire
// e.g. selected, changed
adapter = Utils.Decorate(adapter, EventRelay);

adapter.prototype.render = function() {
// Use selection-box from SingleSelection adapter
// This implementation overrides the default implementation
let $selection = SingleSelection.prototype.render.call(this);
return $selection;
};

adapter.prototype.update = function(data) {
// copy and modify SingleSelection adapter
this.clear();

let $rendered = this.$selection.find(‘.select2-selection__rendered’);
let noItemsSelected = data.length === 0;
let formatted = “”;

if (noItemsSelected) {
formatted = this.options.get(“placeholder”) || “”;
} else {
let itemsData = {
selected: data || [],
all: this.$element.find(“option”) || []
};
// Pass selected and all items to display method
// which calls templateSelection
formatted = this.display(itemsData, $rendered);
}

$rendered.empty().append(formatted);
$rendered.prop(‘title’, formatted);
};

return adapter;
});
Step 2 – create custom dropdown adapter

$.fn.select2.amd.define(“CustomDropdownAdapter”, [
“select2/utils”,
“select2/dropdown”,
“select2/dropdown/attachBody”,
“select2/dropdown/attachContainer”,
“select2/dropdown/search”,
“select2/dropdown/minimumResultsForSearch”
],
function(Utils, Dropdown, AttachBody, AttachContainer, Search, MinimumResultsForSearch) {

// Decorate Dropdown with Search functionalities
let dropdownWithSearch = Utils.Decorate(Dropdown, Search);
dropdownWithSearch.prototype.render = function() {
// Copy and modify default search render method
var $rendered = Dropdown.prototype.render.call(this);
// Add ability for a placeholder in the search box
let placeholder = this.options.get(“placeholderForSearch”) || “”;
var $search = $(
‘ +
” +

);

this.$searchContainer = $search;
this.$search = $search.find(‘input’);

$rendered.prepend($search);
return $rendered;
};

// Decorate the dropdown+search with necessary containers
let adapter = Utils.Decorate(dropdownWithSearch, AttachContainer);
adapter = Utils.Decorate(adapter, AttachBody);

return adapter;
});
Step 3 – use the defined adapters

$(“#multipleWithSearch”).select2({
data: testData,
placeholder: “Select items”,
placeholderForSearch: “Filter items”, // additional placeholder for search box
closeOnSelect: false,
// Make selection-box similar to single select
selectionAdapter: $.fn.select2.amd.require(“CustomSelectionAdapter”),
templateSelection: (data) => {
return `Selected ${data.selected.length} out of ${data.all.length}`;
},
// Add search box in dropdown
dropdownAdapter: $.fn.select2.amd.require(“CustomDropdownAdapter”)
});
Find the complete solution example on jsFiddle.

Summary
This article explains how you can extend Select2 plugin by utilizing its adapters and decorators feature.

To create better custom adapters, try to re-use the built-in adapters, and modify them on the go. By doing that you will learn more about select2 internals, thus your custom adapters will be much cleaner and you will have less code to debug.

Finally, observe the example code on jsFiddle, modify it and see how you can further customize select2 as per your needs.

NOTE: I use the select2 plugin in my Aurelia apps by wrapping common behavior in custom elements. So far, so good. It’s nice that we are able to easily re-use proven components/plugins from other major platforms.

Advertisements
Javascript, jQuery, Web Development

Summarise.js – Make Bootstrap Alerts More… Responsive

I would like to introduce something which has made my life easier when using Bootstrap in dynamic event based situations, especially for XHR forms. I call it: summarise.js. It, essentially, allows you to make Bootstrap alerts which can be controlled entirely in JavaScript. This means you can make them work around XHR calls and other dynamic events.

You can find more at the project’s GitHub repository: https://github.com/Sammaye/summarise

Javascript, jQuery

Amazon like Product Gallery with Prodigal

I recently had a need to create a product gallery like on Amazon capable of using images and videos and having a filter for showing one or the other, or both. So I went off, procrastinated about it for about 3 days then made it in about 1.

I have called it: prodigal.

You can find the GitHub repository to fork, love etc here: https://github.com/Sammaye/prodigal.

The browsers I have tested this on as working are:

  • Firefox (latest)
  • IE (7 and above)
  • Safari 5.1 and above
  • Opera 12 and above
  • and Chrome (latest)

All you need to do to get started is to look at the example.html page within the repository and you can use it, for example:

$('.some-thumbs').prodigal({ //options });

With .some-thumbs being a class that is on all the thumbnails you wish to enter prodigal.

Javascript, jQuery, PHP, Web Development

PHP Multiple Upload Progress Bar With Advanced Upload Control

Note: this article is DEPRECATED. Please use one the many great scripts on the internet, like blueimp’s jQuery file upload plugin.

tl;dr: this is on GitHub: https://github.com/Sammaye/multifile_ajax_uploader

First thing you need to do is install the uploadprogress extension for PHP:

apt-get install php5-pear
apt-get install php5-dev
pecl install uploadprogress

Afterwards open up your php.ini and add:

extension=uploadprogress.so

And, finally, restart (don’t reload) your server:

sudo /etc/init.d/apache2 restart
sudo servicee nginx restart
# etc

Believe it or not, but you now have PHP upload progress identification installed. Now you just need to make a script that will upload a file and display its progress. Shall we move on?

The JavaScript

var upload_timer;
var timeout = 5000;
var u_ids = [];

$(document).ready(function () {

    // Add a new upload form
    add_upload();

    // When the "Add new upload" link is clicked add a new upload form
    $('#add_upload_form').click(function () {
        add_upload();
        return false;
    });

});

/**
 * Stops an upload
 *
 * @param id
 */
function stop_upload(id) {

    var answer = confirm("Are you sure you wish to remove this upload?"), p_id = "#up_prog" + id;

    // Only remove the upload if it has not completed.
    if (answer && ($(p_id + " .uploadProgInner").css("width") != "100%") && ($("#container_outer" + id).length > 0)) {

        // Form ids and write the cancelling message
        var message_id = "#up_message_prog" + id;
        $(message_id).html("<b style='color:red;'>Cancelling...</b>");

        // Make the upload go away peacefully showing the user it has been cancelled.
        setTimeout(function () {
            $("#container_outer" + id).remove();
            u_ids.splice(u_ids.indexOf(id), 1);
        }, "2000");
    } else {
        if (($(id + " .uploadProgInner").css("width") == "100%") || ($("#container_outer" + id).length == 0)) {
            $("#general_error").show();
            $("#general_error span").html("<h2>File Upload Could Not Be Removed</h2><p>The file upload could not be removed. This is most likely due to the file having had already been completed or already removed by some other means.</p>");
        }
    }
}

/**
 * This function runs when a upload is submitted. It checks the
 * file field as best it can for a valid file. TBH there are some things
 * we just have to do AFTER the file has been uploaded to /temp
 *
 * @param id
 * @returns {Boolean}
 */
function check_upload(id) {
    var file = $("#" + id).val(), end = file.length, start = end - 5;

    var ext = file.substring(start, end);

    // Is the file a divx extension?
    if (ext == ".divx") {
        $("#uploadForm-outer" + id).hide();
        $("#up_prog" + id).show();
    } else {
        $("#general_error").show();
        $("#general_error span").html("<h2>Wrong File Type</h2><p>You attempted to upload a file type that is not supported by this uploader. Pleae try again soon, we hope to have support for mkv, avi and other formats in their raw form.</p>");
        return false;
    }
}

/**
 * This adds a new upload form to the screen
 */
function add_upload() {

    var ts, count;

    // We make a ts as a cache buster for shit browsers cough-Opera-cough
    ts = Math.round(new Date().getTime() / 1000);

    // How many forms already exist?
    count = $(".uploadFormContainer").length;

    // So long as limit has not been reached (10)
    if (count < 10) {

        // Generate the form
        $.get("/upload/add", {ts: ts}, function (data) {

            // Add to the page
            $("#uploadForm_container-outer").append(data);

            // If this is the first form then add the updater iframe
            if (count == 0 && $('#uInfo_ifr').length == 0) {
                $("#u_iframe_container").html("<a href=" / upload / getInfo
                ">/upload/getInfo</a>"
            )
                ;
            }

            // Add the upload id to the list of IDs
            var e = $(".uploadFormContainer").last().find("input[name=UPLOAD_IDENTIFIER]").val();
            count_ids = u_ids.length;
            u_ids[count_ids] = e;
        });

    } else {

        // Show upload limit error
        $("#general_error").show();
        $("#general_error span").html("<h2>Upload Limit Reached</h2><p>This form has been limited to 10 uploads to protected internet connections. If you are sure you understand what your ISP allows you to upload you can open a new window and continue uploading there.</p>");
    }
}

/**
 * This gets the updated status of the uploads
 *
 * @param info
 */
function update_progress(info) {

    // Scrolls through the IDs assigning the information.
    for (i = 0; i < info.length; i++) {

        // Sometimes uploadprogress can return null for a upload
        if (info[i] != null) {

            // Calculate how much has been uploaded
            var done = Math.floor(100 * parseInt(info[i].uploaded) / parseInt(info[i].total));

            // Ascertain the IDs for the elements needing change
            var id = "#up_prog" + info[i].id;
            var message_id = "#up_message_prog" + info[i].id;

            // Change the width of the progress bar to match done and set a message for the upload status
            $(id + " .uploadProgInner").css("width", done + "%");
            $(message_id + " span").html(done + "% Completed of " + info[i].file + " - Estimated Time Left: " + info[i].left + " at " + info[i].speed + "ps");
        }
    }

}

The Upload Form

Now we have the JavaScript we need the upload form itself. This form will be rendered every time add_upload() is called within the JavaScript whilst the user is under the upload limit:

<?php
global $session;
$u_id = strval(new MongoId());
?>
<div class="uploadFormContainer" id="container_outer<?php echo $u_id ?>">
    <div class="uploadForm" id="uploadForm-outer<?php echo $u_id ?>">

        <h2>Please select a file to upload</h2>

        <form 
            onsubmit='check_upload("<?php echo $u_id ?>")' 
            action="/upload/to_server" 
            target="u_ifr<?php echo $u_id ?>" 
            method="post" 
            enctype="multipart/form-data"
        >
            <input type="hidden" name="UPLOAD_IDENTIFIER" value="<?php echo $u_id ?>"/>
            <input type="file" name="<?php echo $u_id ?>" id="<?php echo $u_id ?>"/>

            <input type="submit" value="Upload" class="uploadFormSubmit"/>
        </form>


    </div>

    <div class="uploadBar" id="up_prog<?php echo $u_id ?>">
        <div class="uploadProgOuter">
            <div class="uploadProgInner">&nbsp;</div>
        </div>
        <div class="up_message_prog" id="up_message_prog<?php echo $u_id ?>">
            <span>Connecting to server...</span>
            <a href="javascript:;" class="cancel_uploadForm" onclick='stop_upload("<?php echo $u_id ?>")'>
                <b>Cancel</b>
            </a>
        </div>
        <div id="up_err<?php echo $u_id ?>"></div>
    </div>
</div>

The Progress Update Page

Now we have a form up and running (I will let you decipher the form :P) we just need the progress update page:

<?php
// A nice little hack for telling IE we wont abuse iframe permissions
header('P3P: CP="CAO PSA OUR"');
global $session;
?>
<html>
    <head>
        <title>Upload Info Page</title>
    
        <script type="text/javascript">
    
            // Get the u_ids from the parent window
            var u_ids = parent.u_ids, id_url = "";
    
            // Run the initial function
            refreshiframe();
    
            function refreshiframe() {
    
                // Get the url formatted version of u_ids (upload IDs)
                if (u_ids != null && u_ids.length > 0) {
                    for (i = 0; i<u_ids.length; i++) {
                        // return an url of the current id
                        if (i == 0) {
                            id_url = id_url + "ids[]="+u_ids[i];
                        } else {
                            id_url = id_url + "&ids[]="+u_ids[i];
                        }
                    }
                } else {
                    id_url = "ids[]=0";
                }
    
                // if php detects the set $_GET then delay the refresh else do it now
                <?php if (empty($_GET['ids']) && !isset($_GET['ids'])) { ?>
                    redirect();
                <?php }else{ ?>
                    setTimeout("redirect()",5000);
                <?php } ?>
            }
    
            function redirect() {
                // redirect with full url encoded u_ids
                parent.uInfo_ifr.location.href="/upload/getInfo?"+id_url;
            }
        </script>
    
    </head>
    
    <body>
        <script type="text/javascript">
        
            var info = [];
        
            <?php for ($i = 0; $i<count($_GET['ids']); $i++) {
        
                $upload_id = $_GET['ids'][$i];
        
                // This is our magic function. It gets our upload information
                $info = uploadprogress_get_info($upload_id);
        
                // We format the data to be printed into JS JSON arrays and echo it to screen
                if (!empty($info) && $info) { ?>
                    info[<?php echo $i ?>] = {'id':'<?php echo $upload_id ?>', 'file':'<?php echo $info['filename'] ?>', 'uploaded':<?php echo $info['bytes_uploaded'] ?>, 'total':<?php echo $info['bytes_total'] ?>, 'left':'<?php echo gmdate("H:i:s", $info['est_sec']) ?>', 'speed':'<?php echo convert_size_human($info['speed_average']) ?>'};
                <?php } else { ?>
                    info[<?php echo $i ?>] = null;
                <?php } ?>
        
            <?php } ?>
        
            // Now lets update the upload progress
            parent.update_progress(info);
        
        </script>
    </body>
</html>

The Page

Now we need the other page this all goes into:

<div class="UIMessage error UIMessageConstrained UIUploadErrorMessage" id="general_error">
    <span></span>
</div>

<div class="upload_container">

    <div id="uploadForm_container-outer"></div>

    <div id="u_iframe_container" class="u_iframe_container"></div>

    <a href="" id="add_upload_form">Add new upload</a>

</div>

Believe it or not, but that is it! You now know how to make a progress bar in PHP. Remember, this isn’t flash and nor is it HTML 5, it will work on any browser with almost any settings so long as they don’t have JavaScript turned off :P. All you need to do now is style it. Here is my style sheet to get you started:

.upload_container {
    width: 600px;
    margin: auto;
}

.uploadFormContainer {
    padding: 10px;
    border: 1px solid #C9D7F1;
    margin-bottom: 10px;
}

.uploadForm {
    border: 1px solid #E1ECFE;
    background: none repeat scroll 0 0 #F0F6FF;
    padding: 10px;
}

.uploadForm h2 {
    margin-bottom: 10px;
}

.uploadFormSubmit {
    float: right;
    font-weight: bold;
}

.uploadBar {
    display: none;
}

.uploadProgOuter {
    background: white;
    height: 40px;
    padding: 2px;
    border: 1px solid #73A6FF;
}

.uploadProgInner {
    background: #73A6FF;
    width: 10%;
    height: 40px;
}

.up_message_prog {
    margin-top: 7px;
}

.u_iframe_container {
    display: none;
}

.UIUploadErrorMessage {
    display: none;
    margin: 15px auto;
}

This style produces this result (or close anyway):

Any questions? If so, just write a comment, otherwise; enjoy :).

CSS, Javascript, jQuery

Facebook Top Bar/Bottom Bar Sticky Navigation Bar

This snippet shows you how to make a Facebook bottom bar like they used to have before the latest redesign.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Facebook Navigation Example</title>

    <script>
        // This is the JavaScript that controls the menu
        var SubMenutimer;
        var last_o;

        $(".mainbar").ready(function () {
            $(".staticMenu dt a").click(function () {

                $(".staticMenu dd ul").not($(this).parents(".staticMenu").find("ul")).hide();
                $(".staticMenu dt a").not($(this)).removeClass("selected");
                $(this).parents(".staticMenu").find("ul").toggle();

                if ($(this).parents(".staticMenu").find("ul").css("display") == "none") {
                    $(this).removeClass("selected");
                } else {
                    $(this).addClass("selected");
                }

            });

            $(".staticMenu dd ul li a").click(function () {
                var text = $(this).html();
                $(".staticMenu dt a span").html(text);
                $(".staticMenu dd ul").hide();
            });

            $(document).bind('click', function (e) {
                var $clicked = $(e.target);
                if (!$clicked.parents().hasClass("staticMenu")) {
                    $(".staticMenu dd ul").hide();
                    $(".staticMenu dt a").removeClass("selected");
                }

            });
        });

        function openSubMenu(o) {
            cancelSubMenuClose();

            if (last_o) $(last_o).parent().find("div").hide();

            last_o = o;
            $(o).parent().find("div").show();
        }

        function closeSubMenu() {
            SubMenutimer = setTimeout("close()", 500);
        }

        function cancelSubMenuClose() {
            clearTimeout(SubMenutimer);
        }

        function close() {
            $(last_o).parent().find("div").hide();
        }
    </script>

    <style>
        div.mainbar {
            top: 0;
            z-index: 99;
            padding: 0;
            position: fixed;
            width: 100%;
        }

        div.mainbarInner {
            background: url(/images/pre_top.png);
            border: 1px solid #bcb6aa;
            border-top: 0;
            border-bottom: 0;
            margin: 0 15px;
        }

        ul.mainMenu {
            list-style: none;

        }

        ul.mainMenu li {
            float: left;
            border-right: 1px solid #bcb6aa;
        }

        .mainMenu li a {
            padding: 5px 4px 5px 4px;
            margin-bottom: 1px;
            display: block;
            font-weight: bold;
            color: #444444;
            line-height: 15px;
            text-decoration: none;
        }

        .mainMenu li a:hover {
            background: white;
            text-decoration: none;
        }

        .staticMenu dl {
        }

        .staticMenu dd {
            position: relative;
        }

        /* DT styles for sliding doors */
        .staticMenu dt a {
            padding: 5px 4px 5px 4px;
            margin-bottom: 1px;
            display: block;
            font-weight: bold;
            color: #444444;
            line-height: 15px;
            text-decoration: none;
        }

        .staticMenu dt a.selected {
            background: white;
            position: relative;
            z-index: 99;
            padding-bottom: 6px;
        }

        /* UL styles */
        .staticMenu dd ul {
            display: none;
            list-style: none;
            position: absolute;
            cursor: pointer;
        }

        ul.mainMenuSub {
            position: absolute;
            top: -2px;
            left: -1px;
            padding: 2px;
            border: 1px solid #b4a291;
            background: white;
        }

        ul.mainMenuSub li {
            border: 0;
            width: 223px;
        }

        ul.mainMenuSub li.seperator {
            width: 223px;
            height: 2px;
            display: block;
            background: url(/images/mainmenusep.png) repeat-x;
            margin-bottom: 6px;
            margin-top: 4px;
            overflow: visible;
        }

        ul.mainMenuSub li a:link {
            display: block;
            font-weight: bold;
            text-align: left;
            overflow: hidden;
            padding: 2px 4px 3px 19px;
            color: #444444;
            text-decoration: none;
        }

        ul.mainMenuSub li a:hover {
            background: #444444;
            color: white;
            text-decoration: none;
        }

        ul.mainMenuSub li a img {

            border: 0;
        }

        ul.mainMenuSmall li {
            width: 173px;
        }

        div.mainMenuSubTwo {
            display: none;
            left: 220px;
            position: absolute;
            top: 3px;
            z-index: 99;
            background: white;
        }

        div.mainMenuSubTwo ul {
            border: 1px solid #b4a291;
            padding: 2px;
            background: white;
        }

        a.MainSubExpandable {
            background: white url(/images/mainmenuexpandable.png) no-repeat right top;
        }

        ul.mainMenuSub li a.MainSubExpandable:hover {
            background: #444444 url(/images/mainmenuexpandable.png) no-repeat;
            background-position: right -20px;
        }
    </style>

</head>
<body>
    <div class="mainbar">
        <div class="mainbarInner" style="overflow:visible; position:relative; display:block; height:26px;">
            <ul class="mainMenu">
                <li>
                    <dl style="" class="staticMenu">
                        <dt><a href="/applications" onclick="return false;">Applications</a></dt>
                        <dd>
                            <ul class="mainMenuSub">
                                <li><a>There are no applications here</a></li>
                            </ul>
                        </dd>
                    </dl>
                </li>
                <li>
                    <dl style="" class="staticMenu">
                        <dt><a href="/places" onclick="return false;">Places</a></dt>
                        <dd>
                            <ul class="mainMenuSub">
                                <li><a href="javascript:;" onmouseover="openSubMenu(this)" class="MainSubExpandable" onmouseout="closeSubMenu(this)" style="cursor:pointer;">Library</a>

                                    <div class="mainMenuSubTwo" id="subMenuMainLib" onmouseover="cancelSubMenuClose()" onmouseout="closeSubMenu(this)">
                                        <ul>
                                            <li><a href="#">Videos</a></li>
                                            <li><a href="#">Favourites</a></li>
                                            <li><a href="#">Playlists</a></li>
                                            <li><a href="#">Subscriptions</a></li>
                                        </ul>
                                    </div>
                                </li>
                                <li class="seperator"><!-- --></li>
                                <li><a href="javascript:;" class="dropdownglobalchange" id="M" style="cursor:pointer;">Networks</a></li>
                                <li><a href="javascript:;" class="dropdownglobalchange" id="M" style="cursor:pointer;">Groups</a></li>
                                <li><a href="javascript:;" class="dropdownglobalchange" id="M" style="cursor:pointer;">Friends</a></li>
                                <li class="seperator"><!-- --></li>
                                <li><a href="javascript:;" class="dropdownglobalchange" id="M" style="cursor:pointer;">Inbox</a></li>
                                <li><a href="javascript:;" class="dropdownglobalchange" id="F" style="cursor:pointer;">Channel</a></li>
                            </ul>
                        </dd>
                    </dl>
                </li>
                <li>
                    <dl style="" class="staticMenu">
                        <dt><a href="/settings" onclick="return false;">Settings</a></dt>
                        <dd>
                            <ul class="mainMenuSub">
                                <li><a href="javascript:;" onmouseover="openSubMenu(this)" class="MainSubExpandable" onmouseout="closeSubMenu(this)" style="cursor:pointer;">Preferences</a>
                                    <div class="mainMenuSubTwo" id="subMenuMainLib" onmouseover="cancelSubMenuClose()" onmouseout="closeSubMenu(this)">
                                        <ul>
                                            <li><a href="#">About Me</a></li>
                                            <li><a href="#">Appearence</a></li>
                                            <li><a href="#">Main Menu</a></li>
                                            <li><a href="#">Privacy</a></li>
                                            <li><a href="#">Notifications</a></li>
                                            <li><a href="#">Adverts</a></li>
                                            <li><a href="#">Safe Search</a></li>
                                        </ul>
                                    </div>
                                </li>
                                <li><a href="javascript:;" onmouseover="openSubMenu(this)" class="MainSubExpandable" onmouseout="closeSubMenu(this)" style="cursor:pointer;">Administration</a>
                                    <div class="mainMenuSubTwo" id="subMenuMainLib" onmouseover="cancelSubMenuClose()" onmouseout="closeSubMenu(this)">
                                        <ul>
                                            <li><a href="#">Block List</a></li>
                                            <li><a href="#">Parental Controls</a></li>
                                            <li><a href="#">Email Options</a></li>
                                            <li><a href="#">Username</a></li>
                                            <li><a href="#">Password</a></li>
                                            <li><a href="#">Payments</a></li>
                                            <li><a href="#">Linked Accounts</a></li>
                                            <li><a href="#">Security Question</a></li>
                                            <li><a href="#">Deactivate Account</a></li>
                                        </ul>
                                    </div>
                                </li>
                                <li class="seperator"><!-- --></li>
                                <li><a href="javascript:;" class="dropdownglobalchange" id="M" style="cursor:pointer;">Help Center</a></li>
                                <li><a href="javascript:;" class="dropdownglobalchange" id="M" style="cursor:pointer;">About StageX</a></li>
                                <li class="seperator"><!-- --></li>
                                <li><a href="javascript:;" class="dropdownglobalchange" id="M" style="cursor:pointer;">Developers</a></li>
                            </ul>
                        </dd>
                    </dl>
                </li>
            </ul>
            <div style='float:right;'>
                <ul class="mainMenu" style="float:right;">
                    <li style="border-right:0;">
                        <dl class="staticMenu">
                            <dt><a href="/settings" onclick="return false;">Sam</a></dt>
                            <dd>
                                <ul class="mainMenuSub" style="right:-1px; left:auto;">
                                    <li><a href="javascript:;" class="dropdownglobalchange" id="A" style="cursor:pointer;">Available</a></li>
                                    <li><a href="javascript:;" class="dropdownglobalchange" id="F" style="cursor:pointer;">Busy</a></li>
                                    <li><a href="javascript:;" class="dropdownglobalchange" id="F" style="cursor:pointer;">Away</a></li>
                                    <li><a href="javascript:;" class="dropdownglobalchange" id="F" style="cursor:pointer;">Appear Offline</a></li>
                                    <li class="seperator"><!-- --></li>
                                    <li><a href="javascript:;" class="dropdownglobalchange" id="M" style="cursor:pointer;">Log out</a></li>
                                    <li><a href="javascript:;" class="dropdownglobalchange" id="M" style="cursor:pointer;">Switch User</a></li>
                                </ul>
                            </dd>
                        </dl>
                    </li>
                </ul>
                <div class="notificationArea" style='float:right;'>
                    Notification Icons
                </div>
            </div>
        </div>
    </div>
</body>
</html>
CSS, Javascript

Creating a JavaScript Overlay Window (Modal)

This tutorial will enable the reader to create CSS div overlay dialogues using JavaScript. This tutorial was taken from another blog, however, since I first visited the blog it has lost most of the tutorial. I had to revisit a previous revision of my website in order to know how to do this so I thought I would make a new post on my blog to keep the knowledge going.

Now, the main thing to understand is that there are extensions that will do this for you, however, I did not use these as they didn’t work… Yes, that’s right; they didn’t work (due to certain JavaScript methods and where the window was being called from) so I revived the home made way.

First thing’s first we need to create two divs, one to darken the screen and one for the dialogue. I should also mention this is easy to convert into a Facebook style message box if you simply make rounded corners for the divs (CSS can do it but only on some browsers and not many, this is how Facebook do it) and making the back dark div only a little bigger than the message window (add a padding or margin).

The first two divs look like this:

<style>
    .darkenBackground {
        background-color: rgb(0, 0, 0);
        opacity: 0.7; /* Safari, Opera */
        -moz-opacity:0.70; /* FireFox */
        filter: alpha(opacity=70); /* IE */
        z-index: 20;
        height: 100%;
        width: 100%;
        background-repeat:repeat;
        position:fixed;
        top: 0px;
        left: 0px;
    }

    .loginboxhover{
        z-index: 20;
        position:fixed;
    }
</style>

<div id="darkBackgroundLayer" class="darkenBackground" style="display:none;"></div>

<div id="exitConfirm_D" class="loginboxhover" style="display:none;" align="center">
    <div id="uploadconfirmleave" style="width:712px; z-index:1;">
        <div class="defaultContainerOuter">
            <h2 class="regTop" id="">Hey You! Yeah! You There!</h2>
            <div class="defaultContainerInner" style="text-align:left; padding:0;">
                <div style="padding-left:5px; padding-right:5px;">
                    <p>
                        This page is attempting to redirect away from itself to another page. In order to stop my servers from exploding this action will stop any files you are currently uploading.
                    </p>
                    <p>Are you sure you wish to navigate away from this page?</p>
                </div>
                <div style="background:#131313; border-top:1px solid #363636; padding:5px; padding-top:7px; color:#fff;">
                    <form action="usrcp.php?home">
                        <div align="right">
                            <input type="submit" value="Yes, Take Me There" class="button7" style="margin-right:3px;">
                            Or <a id="N_RtnUP" class="normalHyp" style="cursor:pointer;">
                                No, Take Me Back to The Uploads
                            </a>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>

As you will notice this has been taken directly from one of my scripts. The first div is darkBackgroundLayer, this is the one that darkens our screen. The second is exitConfirm_D which holds a lot of code. This is the message box. Everything within the main div is just for example reasons.

Now, what we need to do is a good old hide and show:

function ShowExitConfirmPanel(){

    document.getElementById("darkBackgroundLayer").style.display = "block";
    document.getElementById("exitConfirm_D").style.display = "block";

    var newsletter_panel = document.getElementById("exitConfirm_D");

    w = 700;
    h = 346;

    xc = Math.round((document.body.clientWidth/2)-(w/2));
    yc = Math.round((document.body.clientHeight/2)-(h/2));

    var height = yc.toString();
    var temp = height.split("-");

    newsletter_panel.style.left = xc + "px";
    newsletter_panel.style.top = "10" + "em";

}

And hey, presto! The function simply grabs the two divs by their ids, sets their display to block (from none) and then changes the height and position of the main message box to suit our needs.

To close the message box simply use:

function closeit(){
    document.getElementById("darkBackgroundLayer").style.display = "none";
    document.getElementById("exitConfirm_D").style.display = "none";

};