/******************************************************************************
    TextUtils.js
    Copyright (c) 2006-2008 Motorola, Inc.
    All rights reserved
******************************************************************************/
$package("netopia/textutils.html");

//-----------------------------------------------------------------------------

netopia.create("text").add({

//--------------------
// see http://www.regular-expressions.info/email.html
kEmailRegEx : /^[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}$/i,

/**
Replaces numbered placeholder targets with corresponding argument values. The
placeholder targets are of the form "$(1)", "$(2)", etc. where the number is the
position in the argument list for the replacement value.

For example:

    var s = netopia.text.format("This $(1) is a test of the $(2).", "foo", "bar");

    alert(s); // displays "This foo is a test of the bar."

Typically, the fmt parameter will come from a localized string.

All occurances of a placeholder are replaced (multiple occurances of "$(1)" are
valid). Only placeholders that have replacement values given to this method will
be replaced. In the above example, "$(3)", "$(4)" and so on would remain in the
result.
*/
format : function ()
{
    var regExCache = [ ];

    return function (fmt)
    {
        var s = fmt;

        for (var i = 1; i < arguments.length; ++i)
        {
            var re = regExCache[i];
            if (!re)
                regExCache[i] = re = new RegExp("\\$\\("+i+"\\)", "g");

            s = s.replace(re, arguments[i]);
        }

        return s;
    };
}(),

/**
Appends a word (or words) to a base separated by a string. By default, the
separator string is a single space. The base may be undefined, null or empty,
in which case the word(s) to add are returned. If the words are in an array,
they are joined using the given separator.

@param base The base text on which to append the word(s).
@param word The word(s) to append (may be a string or array of strings).
@param sep The separator between each word.
@return The value of "base + sep + word", give or take special cases.
*/
appendWord : function (base, word, sep)
{
    if (word === undefined || word == null || word.length == 0)
        return base;

    sep = sep || " ";

    if (word instanceof Array)
        word = word.join(sep);

    if (base === undefined || base == null || base.length == 0)
        return word;

    return base + sep + word;
},

/**
Matches a given array of words against a delimited string of words. All words
in the match set must be present in the words string.

For example:
    matchAllWords(["foo", "bar"], "foo da bar"); // => true
    matchAllWords(["foo", "bar"], "foo da"); // => false
    matchAllWords(["foo", "bar"], "bar the foo"); // => true

@param match The array of words to match.
@param sep The (optional) word separator (defaults to " ").
@param words The string of words separated by sep.
@return True if all match words are present in words.
*/
matchAllWords : function (match, sep, words)
{
    if (arguments.length == 2) // 2 arg form is (match, words)
    {
        words = sep;
        sep = null;
    }

    return netopia.text.matchWords(match, false, sep, words);
},

/**
Matches a given array of words against a delimited string of words.

For example:
    matchAnyWords(["foo", "bar"], "foo da bar"); // => true
    matchAnyWords(["foo", "bar"], "foo da"); // => true
    matchAnyWords(["foo", "bar"], "bar"); // => true
    matchAnyWords(["foo", "bar"], "the jazz"); // => false

@param match The array of words to match.
@param sep The (optional) word separator (defaults to " ").
@param words The string of words separated by sep.
@return True if any of the match words are present in words.
*/
matchAnyWords : function (match, sep, words)
{
    if (arguments.length == 2) // 2 arg form is (match, words)
    {
        words = sep;
        sep = null;
    }

    return netopia.text.matchWords(match, true, sep, words);
},

/**
Matches a given array of words against a delimited string of words.

@param match The array of words to match.
@param any True to match any words, false to match all words.
@param sep The (optional) word separator (defaults to " ").
@param words The string of words separated by sep.
@return True if any/all match words are present in words.
*/
matchWords : function (match, any, sep, words)
{
    if (words === undefined || words == null || words.length == 0)
        return false;

    if (arguments.length == 3) // 3 arg form is (match, any, words)
    {
        words = sep;
        sep = null;
    }
    
    sep = sep || " ";

    var parts = words.split(sep);
    if (!(match instanceof Array))
        match = match.split(sep);

    for (var n = match.length; n-- > 0; )
    {
        var found = false;

        for (var k = parts.length; k-- > 0 && !found; )
            found = (match[n] == parts[k]);

        if (found == any) // odd looking, but correct...
            return any;
    }

    return !any;
},

/**
Removes all occurances of a word or words from a string.

@param base The base text from which to remove the word(s).
@param words The word or words to remove (may be a string or array of string).
@param sep The separator between each word.
@return The "base" string with each word in "words" removed.
*/
removeWords : function (base, words, sep)
{
    if (words === undefined || words == null || words.length == 0)
        return base;

    var ret = null;

    sep = sep || " ";

    if (base !== undefined && base != null && base.length > 0)
    {
        var arr = base.split(sep);

        if (!(words instanceof Array))
            words = words.split(sep);

        for (var i = 0; i < arr.length; ++i)
            if (netopia.core.findMember(words, arr[i]) === undefined)
                ret = (ret == null) ? arr[i] : (ret + sep + arr[i]);
    }

    return ret;
},

regexMatchesAll : function (re, str)
{
    var v = str || "", m = re.exec(v);
    if (!m || m.index != 0 || m[0] != v)
        return false;
    return true;
},

/**
Removes one or more words and adds other word or words to a string. This is
essentially equivalent to the following:

    return appendWord(removeWords(base, remove, sep), add, sep);

@param base The base text on which to append/remove the word(s).
@param remove The word(s) to remove.
@param add The word(s) to add.
@param sep The separator between each word.
@return The updated string.
*/
replaceWords : function (base, remove, add, sep)
{
    var s = netopia.text.removeWords(base, remove, sep);
    var t = netopia.text.appendWord(s, add, sep);
    return t;
},

//--------------------------------------
// HTML text

escapeHtml : function (s)
{
    return s.replace(/&/g,"&amp;").replace(/>/g,"&gt;").replace(/</g,"&lt;").
             replace(/"/g,"&quot;");
},

stripHtmlTags : function (s)
{
    return s.replace(/<[^>]+>|&nbsp;/g,"");
},

/* reverts the result of the escapeHtml function */
unescapeHtml : function (s)
{
    return s.replace(/&amp;/g,"&").replace(/&gt;/g,">").replace(/&lt;/g,"<").
             replace(/&quot;/g,"\"");
},

kTagRegEx : new RegExp("</?\\w+((\\s+\\w+(\\s*=\\s*(?:\"(.|\\n)*?\"|'(.|\\n)*?'|[^'\">\\s]+))?)+\\s*|\\s*)/?>", "i"),
kAttrRegEx : new RegExp("\\s*(\\w+)(?:\\s*=\\s*(\"(.|\n)*?\"|'(.|\n)*?'|[^'\"<\\s]+))?", "gi"),

getHtmlSection : function (content, tag, fromIndex)
{
    var j = content.indexOf("<"+tag, fromIndex);
    if (j < 0)
        return null;
    var s = content.substr(j);
    var m2, m = s.match(netopia.text.kTagRegEx);
    if (!m)
        return null;
    var ret = { tagBegin     : j,
                attrs        : { } };
    if (m[0].substr(m[0].length - 2) == "/>")
    {
        ret.tagEnd = j + m[0].length;
        ret.contentBegin = ret.contentEnd = ret.tagEnd;
        ret.content = "";
    }
    else
    {
        var k = content.indexOf("</"+tag+">", j);
        if (k < 0)
            return null;
        ret.tagEnd       = k + tag.length + 3;
        ret.contentBegin = j + m[0].length;
        ret.contentEnd   = k;
        ret.content      = content.substring(ret.contentBegin, ret.contentEnd);
    }

    var re = netopia.text.kAttrRegEx;
    while ((m2 = re.exec(m[1])) != null)
    {
        var t = m2[2], c = t ? t.charAt(0) : "";
        ret.attrs[m2[1]] = (c == '"' || c == "'") ? t.substr(1, t.length-2) : t;
    }

    return ret;
},

//--------------------------------------

wrapInLink : function (text, href, popup, popupName)
{
    if (!href)
        href = text;
    var s = netopia.text.escapeHtml(text);
    var p = "";
    if (popup)
    {
        p = "\" onclick=\"netopia.dhtml.openInNewWindow('"+href+"',";
        p += (typeof(popup) == "string" ? ("'"+popup+"'") : "null");
        p += (popupName ? (",'"+popupName+"'") : "");
        p += "); return false;";
    }
    s = "<a href=\""+href+p+"\">"+s+"</a>";
    return s;
},

//--------------------------------------
// Sortable

Sortable : $extends (netopia.core.Object,
{
    compare : function (a, b)
    {
        return (a < b) ? -1 : (b < a);
    },

    filter : function (v)
    {
        return v;
    },

    getData : function (from)
    {
        if (!from)
            return null;
        var v = from.innerHTML;
        return this.filter(v);
    }
})

}); //netopia.text

netopia.text.add({

//-------------------------------------------
// Sortable types

SortHtml : $extends (netopia.text.Sortable,
{
    filter : function (v)
    {
        return netopia.text.stripHtmlTags(v);
    }
})

}); // netopia.text

netopia.text.add({

//---------------------------

SortDate : $extends (netopia.text.SortHtml,
{
    filter : function (v)
    {
        var v2 = $super(arguments).call(this, v);
        return new Date(v2);
    }
}),

//---------------------------

SortNum : $extends (netopia.text.SortHtml,
{
    filter : function (v)
    {
        var v2 = $super(arguments).call(this, v);
        return (v2.length == 0) ? 0xFFFFFFFF : (v2 * 1); // make numeric
    }
})

}); //netopia.text

