Monthly Archives: September 2015

TinyMCE 3 Plugin: numalign

editor_plugin.js:

/*
 * This plugin allows centered alignment for numeric tabular data.
 * For example:
 *
 * +---------------------+
 * |              1      |
 * +---------------------+
 * |          1,000      |
 * +---------------------+
 * |      1,000,000      |
 * +---------------------+
 *
 * This is achieved by wrapping the data in a right-aligned span, which is itself centered within the cell:
 *
 * <!-- Before -->
 * <td>1,000</td>
 *
 * <!-- After -->
 * <td style="text-align: center;">
 *     <span style="display: inline-block; text-align: right; white-space: nowrap; width: 5em;">1,000</span>
 * </td>
 *
 * All spans in the column must be given an explicit (user-specified) width that can accommodate the widest data point.
 * Some experimentation may be required to determine the appropriate width.
 */

tinymce.create("tinymce.plugins.NumAlignPlugin", {
    init: function (editor, url) {
        editor.addCommand("mceNumAlign", function () {
            editor.windowManager.open({
                file: url + "/dialog.html",
                width: 240,
                height: 80,
                inline: true
            }, {
                plugin_url: url
            });
        });
        editor.addButton("numalign", {
            title: "Align Numbers",
            cmd: "mceNumAlign",
            image: url + "/button.gif"
        });
        editor.onNodeChange.add(function (editor, controlManager, node) {
            var cell = editor.dom.getParent(node, "td, th");
            controlManager.setDisabled("numalign", cell === null);
        });
    }
});
tinymce.PluginManager.add("numalign", tinymce.plugins.NumAlignPlugin);

dialog.html:

<!DOCTYPE html>
<html>
    <head>
        <title>Align Numbers</title>
        <script type="text/javascript" src="../../tiny_mce_popup.js"></script>
        <script type="text/javascript" src="dialog.js"></script>
    </head>
    <body>
        <form>
            <label>
                Width:
                <input type="text" id="width" name="width" />
            </label>
            <div class="mceActionPanel">
                <input type="submit" id="insert" value="OK" />
                <input type="button" id="cancel" value="Cancel" />
            </div>
        </form>
    </body>
</html>

dialog.js:

var P = tinyMCEPopup;

// Initializes the dialog.
function init() {
    var width = P.dom.get("width");
    width.value = P.getWindowArg("width", "5em");
    width.select();
    P.dom.bind(P.dom.get("insert"), "click", update);
    P.dom.bind(P.dom.get("cancel"), "click", close);
}

// Aligns data in the currently selected column and closes the dialog.
function update() {
    var cell = P.editor.dom.getParent(P.editor.selection.getStart(), "td, th");
    var index = getIndex(cell);
    var table = P.editor.dom.getParent(cell, "table");
    var rows = table.getElementsByTagName("tr");
    for (var i = 0; i < rows.length; i++) {
        var cell = getCell(rows[i], index);
        if (cell.nodeName === "TD") {
            align(cell, P.dom.get("width").value);
        }
    }
    P.editor.execCommand("mceEndUndoLevel");
    close();
}

// Returns the column index of the given cell.
function getIndex(cell) {
    var index = 0;
    var child = P.editor.dom.getParent(cell, "tr").firstElementChild;
    while (child !== null) {
        if (child === cell) {
            return index;
        }
        index += getColSpan(child);
        child = child.nextElementSibling;
    }
    return -1;
}

// Returns the cell at the given column index.
function getCell(row, index) {
    var current = 0;
    var child = row.firstElementChild;
    while (child !== null) {
        var next = current + getColSpan(child);
        if (current <= index && index < next) {
            return child;
        }
        current = next;
        child = child.nextElementSibling;
    }
    return null;
}

// If the given node is a cell, returns the column span.
// Otherwise, returns zero.
function getColSpan(node) {
    if (node.nodeName === "TD" || node.nodeName === "TH") {
        return node.colSpan;
    } else {
        return 0;
    }
}

// Updates the given cell's contents and styles for centered numeric alignment.
function align(cell, width) {
    P.editor.dom.setStyle(cell, "text-align", "center");
    
    // We need the cell's contents to be wrapped in a span
    // If the cell contains only a single span (ignoring leading and trailing whitespace text nodes), then use this span
    // (This allows us to run the plugin repeatedly on the same column)
    // Otherwise, remove the cell's child nodes, add them to a newly created span, and add the span to the cell
    var nodes = trim(cell.childNodes);
    var span;
    if (nodes.length === 1 && nodes[0].nodeName === "SPAN") {
        span = nodes[0];
    } else {
        nodes = P.editor.dom.remove(cell.childNodes);
        span = P.editor.dom.create("span");
        for (var i = 0; i < nodes.length; i++) {
            P.editor.dom.add(span, nodes[i]);
        }
        P.editor.dom.add(cell, span);
    }
    
    P.editor.dom.setStyle(span, "display", "inline-block");
    P.editor.dom.setStyle(span, "text-align", "right");
    P.editor.dom.setStyle(span, "white-space", "nowrap");
    P.editor.dom.setStyle(span, "width", width);
}

// Removes leading and trailing whitespace text nodes.
function trim(nodes) {
    if (nodes.length === 0) {
        return [];
    }
    var start = 0;
    var end = nodes.length;
    while (start < end && isWhitespace(nodes[start])) {
        start++;
    }
    while (end > start && isWhitespace(nodes[end - 1])) {
        end--;
    }
    var result = new Array(end - start);
    for (var i = 0; i < result.length; i++) {
        result[i] = nodes[start + i];
    }
    return result;
}

// Returns whether the given node is a whitespace text node.
function isWhitespace(node) {
    return node.nodeName === "#text" && node.textContent.trim().length === 0;
}

// Closes the dialog.
function close() {
    P.close();
}

P.onInit.add(init);

Download

Scripting Microsoft Access

using System;
using System.Collections.Generic;
using System.Data.OleDb;
using System.IO;

namespace AccessScripter
{
    internal static class Program
    {
        private static IDictionary<string, string> providers = new Dictionary<string, string>
        {
            { ".mdb", "Microsoft.Jet.OLEDB.4.0" },
            { ".accdb", "Microsoft.ACE.OLEDB.12.0" }
        };

        public static void Main(string[] args)
        {
            try
            {
                if (args.Length < 1 || args.Length > 2)
                {
                    Console.Error.WriteLine("Usage: {0} DATABASE [SCRIPT]", Environment.GetCommandLineArgs()[0]);
                    Environment.Exit(1);
                }
                string connectionString = GetConnectionString(args[0]);
                string script = args.Length == 2 ? File.ReadAllText(args[1]) : Console.In.ReadToEnd();
                using (OleDbConnection connection = new OleDbConnection(connectionString))
                {
                    connection.Open();
                    foreach (string sql in script.Split(';'))
                    {
                        if (string.IsNullOrWhiteSpace(sql))
                        {
                            continue;
                        }
                        Console.Error.WriteLine();
                        Console.Error.WriteLine(sql.Trim());
                        try
                        {
                            using (OleDbCommand command = new OleDbCommand(sql, connection))
                            {
                                int count = command.ExecuteNonQuery();
                                Console.Error.WriteLine("{0} record(s) affected.", count);
                            }
                        }
                        catch (Exception ex)
                        {
                            Console.Error.WriteLine("ERROR: {0}", ex.Message);
                            Console.Error.Write("Continue? ");
                            ConsoleKey key = Console.ReadKey(true).Key;
                            Console.Error.WriteLine();
                            if (key != ConsoleKey.Y)
                            {
                                break;
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine(ex);
            }
        }

        private static string GetConnectionString(string dataSource)
        {
            FileInfo file = new FileInfo(dataSource);
            OleDbConnectionStringBuilder connectionString = new OleDbConnectionStringBuilder();
            string provider;
            if (!providers.TryGetValue(file.Extension.ToLower(), out provider))
            {
                throw new ArgumentException(string.Format("Unrecognized file extension '{0}'.", file.Extension));
            }
            connectionString.Provider = provider;
            connectionString.DataSource = dataSource;
            return connectionString.ConnectionString;
        }
    }
}