/*
An Ox.doc comment looks like this `//@ foo` or
```
/*@
foo
bar
baz
*\/
```
The syntax is simple: almost every line has the form `name <type> summary`. If
it doesn't, its meaning depends on its context. 
*/

this.My = {};

/*
If the first line of the comment doesn't match `name <type> summary`, it is a
section definition. Here, we mark a section named 'Primitives'.
*/
//@ Primitives

//@ My.REQUEST_TIMEOUT <number> Request timeout, in milliseconds
My.REQUEST_TIMEOUT = 60000;

/*@
My.MAGIC_CONSTANT <number> Magic constant, needed for HTTP requests
    Please note that the value for `MAGIC_CONSTANT` is browser-dependent.
*/
My.MAGIC_CONSTANT = navigator.userAgent.length % 2 == 0 ? 23 : 42;

//@ Objects

/*
*/
/*@
My.favorites <object> ...
    # Properties  --------------------------------------------------------------
    array       <a> My favorite array
    boolean     <b> My favorite boolean value
    date        <d> My favorite date
    error       <e> My favorite error
    function    <f> My favorite function
    arguments   <g> My favorite arguments
    htmlelement <h> My favorite HTML element
    nodelist    <l> My favorite nodelist
    number      <n> My favorite number
    object      <o> My favorite object
    regexp      <r> My favorite regular expression
    string      <s> My favorite string
    undefined   <u> Undefined is an all-time favorite
    window      <w> ...
    other       <+> ...
    any         <*> Favorite of the day
    # Events -------------------------------------------------------------------
    event       <!> Fires when My.favorite['function'] is called
*/
My.favorites = (function() {
    var favorites = {
            array: [],
            boolean: false,
            date: new Date(),
            error: new Error(),
            'function': function() {
                My.triggerEvent(this, 'event');
            },
            arguments: (function() { return arguments; }()),
            htmlelement: document.createElement('a'),
            nodelist: document.getElementsByTagName('a'),
            number: 0,
            object: {},
            regexp: new RegExp(),
            string: '',
            'undefined': void 0,
            'window': window,
            other: document
        },
        keys = Object.keys(favorites);
    return Ox.extend(favorites, {any: favorites[keys[Ox.random(keys.length)]]});
}());

/*@
        replace <array> ...
            0 <regexp> ...
            1 <string> replace
*/
My.HTMLUtils = {
    entities: {
        '"': '&quot;', '&': '&amp;', "'": '&apos;', '<': '&lt;', '>': '&gt;'
    },
    replace: {
        namedEntity: [
            new RegExp('(' + Ox.values(this.entities).join('|') + ')', 'g'),
            function(match) {
                return Ox.keyOf(this.entities, match);
            }
        ],
        numericEntity: [
            /&#([0-9A-FX]+);/gi,
            function(match, code) {
                return Ox.char(
                    /^X/i.test(code)
                        ? parseInt(code.slice(1), 16)
                        : parseInt(code, 10)
                );
            }
        ],
    }
};

/*@
My.defaultPlace <o> Default place object
    geometry <o>
        bounds <o> ..
            northEast <o>
                lat <n>
                lng <n>
            southWest <o>
                lat <n>
                lng <n>
    types <[s]>
    name <o> Localized place names
        short <o> Short place name
            de <s> Short German name
            en <s> Short English name
            fr <s> Short French name
        long <o> Long place name
            de <s> Long German name
            en <s> Long English name
            fr <s> Long French name
    points <[o]> Points of interest
        lat <n> Latitude
        lng <n> Longitude
@*/
My.place = {
    bounds: {
        northEast: {lat: 0, lng: 0},
        southWest: {lat: 0, lng: 0}
    },
    name: {
        short: {
            de: 'Brüssel',
            en: 'Brussels',
            fr: 'Bruxelles'
        },
        long: {
            de: 'Brüssel, Belgien',
            en: 'Brussels, Belgium',
            fr: 'Bruxelles, Belgique'
        }
    },
    points: [
        {lat: 0, lng: 0},
        {lat: 45, lng: 90}
    ]
};

//@ Functions

/*@
    (url[, method], callback) -> <u> undefined
    url      <s>       URL
    method   <s|'GET'> Request method ('GET', 'POST', 'PUT' or 'DELETE')
    callback <f>       Callback function
*/
My.readURL = function(url, method, callback) {
    var request = new XMLHttpRequest();
    if (arguments.length == 2) {
        callback = method;
        method = 'GET';
    }
    request.open(method, url, true);
    req.onreadystatechange = function() {
        if (request.readyState == 4) {
            if (request.status == 200) {
                callback(request.responseText);
            } else {
                throw new Error(
                    'Cannot get URL "' + url
                    + '" (Status: ' + request.status + ')'
                );
            }
        }
    };
    request.send();
};

/*@
My.isOdd <f> Synchronously or asynchronously computes if a given number is odd
    (number) -> <b> True if the number is odd
    (number, callback) -> <u> undefined
    number <n> Any number
    callback <f> Callback function
        isOdd <b> True if the number is odd
        ms <n> Time it took to compute the result, in milliseconds
*/
My.isOdd = function(number, callback) {
    var time = +new Date, isOdd = !!(number % 2);
    if (callback) {
        callback(isOdd, +new Date - time);
    } else {
        return isOdd;
    }
};

/*
Occasionally, you may write a function whose signature cannot be represented in
`(required[, optional])` notation. For a range function — `(stop)` or `(start,
stop)` or `(start, stop, step)` — the notation `([start, ]stop[, step])` would
be ambigious, since you cannot call it with `(stop, step)`.
*/
/*@
My.range <f> Returns a python-style range
    (b)       -> <[n]> Integers from 0 (inclusive) to b (exclusive)
    (a, b)    -> <[n]> Integers from a (inclusice) to b (exclusive)
    (a, b, c) -> <[n]> Numbers from a (inclusive) to b (exclusive), growing by c
*/
My.range = function() {
    var a = [];
    Ox.loop.apply(null, Ox.toArray(arguments).concat(function(i) {
        a.push(i);
    }));
    return a;
};

/*@
My.localStorage <f> Returns a localStorage handler for a given namespace
    (ns) -> storage <f> localStorage handler
        () -> <o> Returns all key:value pairs
        (key) -> <*> Returns one value
        (key, value) -> <f> Sets one value, returns the handler
        ({key: value, ...}) -> <f> Sets one or more values, returns the handler
        key <s> Any string
        value <*> Any value that can be JSON-serialized
        .delete <f> Delete method
            () -> <f> Deletes all key:value pairs, returns the handler
            (key[, ...]) -> <f> Deletes one or more pairs, returns the handler
            key <s> Any string
    ns <s> Namespace
*/
My.localStorage = (function() {
    if (!window.localStorage) {
        window.localStorage = {};
    }
    return function(ns) {
        function storage(key, value) {
            var args, ret;
            if (arguments.length == 0) {
                ret = {};
                Ox.forEach(localStorage, function(value, key) {
                    if (Ox.startsWith(key, ns + '.')) {
                        ret[key.slice(ns.length + 1)] = JSON.parse(value);
                    }
                });
            } else if (arguments.length == 1 && !Ox.isObject(key)) {
                value = localStorage[ns + '.' + key];
                ret = Ox.isUndefined(value) ? void 0 : JSON.parse(value);
            } else {
                Ox.forEach(Ox.makeObject(arguments), function(value, key) {
                    localStorage[ns + '.' + key] = JSON.stringify(value);
                });
                ret = this;
            }
            return ret;
        };
        storage.delete = function() {
            var keys = arguments.length == 0 ? Object.keys(storage())
                : Ox.toArray(arguments)
            keys.forEach(function(key) {
                delete localStorage[ns + '.' + key];
            });
            return storage;
        };
        return storage;
    };
}());

//@
Ox.load('UI', function() {
    var path = Ox.PATH + '../examples/documentation/js/';
    Ox.print(path, '??')
    Ox.DocPanel({
        files: 'example.js',
        path: path
    }).appendTo(Ox.$body);
});
