<commit_before>/**
 * @file Interface wrapper
 */
/*#ifndef(UMD)*/
(function () {/* Begin of privacy scope */
    "use strict";
    /*global _gpfAssert*/ // Assertion method
    /*global _gpfDefine*/ // Shortcut for gpf.define
/*#endif*/

    var
        gpfI = gpf.interfaces,
        gpfFireEvent = gpf.events.fire,
        _wrappers = {},

        /**
         * Build the synchronous member (no event)
         *
         * @param {String} name
         * @return {Function}
         * @closure
         * @private
         */
        _sync = function (name) {
            return function () {
                this._calls.push(new MethodCall(true, name, arguments, 0));
                this._start();
                return this;
            };
        },

        /**
         * Build the asynchronous member (i.e. event related)
         *
         * @param {String} name
         * @param {Number} length argument count in the function (used to deduce
         * where the event handler is)
         * @return {Function}
         * @closure
         * @private
         */
        _async = function (name, length) {
            return function () {
                this._calls.push(new MethodCall(false, name, arguments,
                    length));
                this._start();
                return this;
            };
        },

        _buildMembers = function (interfaceDef) {
            var
                result = {
                    "+": {},
                    "~": {
                        interface: interfaceDef
                    }
                },
                publicMembers = result.public,
                attributes = new gpf.attributes.Map(),
                prototype = interfaceDef.prototype,
                member,
                method;
            attributes.fillFromClassDef(gpf.classDef(interfaceDef));
            for (member in prototype) {
                if (prototype.hasOwnProperty(member)) {
                    method = prototype[member];
                    if ("constructor" === member) {
                        continue; // ignore
                    }
                    _gpfAssert("function" === typeof method, "Only methods");
                    if (attributes.member(member)
                        .has(gpf.attributes.ClassEventHandlerAttribute)) {
                        publicMembers[member] = _async(member, method.length);
                    } else {
                        publicMembers[member] = _sync(member);
                    }
                }
            }
            return result;
        },

        MethodCall = _gpfDefine("MethodCall", {

            "-": {

                /**
                 * Call is synchronous
                 *
                 * @type {Boolean}
                 * @private
                 */
                _synchronous: true,

                /**
                 * Method name
                 *
                 * @type {String}
                 * @private
                 */
                _name: "",

                /**
                 * Method arguments
                 *
                 * @type {Array}
                 * @private
                 */
                _args: [],

                /**
                 * When asynchronous, length provides the position of the event
                 * handler
                 *
                 * @type {Number}
                 * @private
                 */
                _length: 0

            },

            "+": {

                /**
                 *
                 * @param {Boolean} synchronous Call is synchronous
                 * @param {String} name Method name
                 * @param {Array} args Method arguments
                 * @param {Number} length When asynchronous, length provides the
                 * position of the event handler
                 */
                constructor: function (synchronous, name, args, length) {
                    this._synchronous = synchronous;
                    this._name = name;
                    this._args = args;
                    this._length = length;
                },

                /**
                 * Apply the call
                 *
                 * @param {Object} iHandler
                 * @param {Function} callback
                 */
                apply: function (iHandler, callback) {
                    var
                        args = this._args,
                        method = iHandler[this._name],
                        finalArgs,
                        count,
                        idx;
                    if (this._synchronous) {
                        method.apply(iHandler, args);
                        callback.call();
                    } else {
                        count = this._length;
                        finalArgs = new Array(count);
                        finalArgs[--count] = callback;
                        if (count > args.length) {
                            count = args.length;
                        }
                        for (idx = 0; idx < count; ++idx) {
                            finalArgs[idx] = args[idx];
                        }
                        method.apply(iHandler, finalArgs);
                    }
                }

            }

        }),

        WrapInterface = _gpfDefine("WrapInterface", {

            "-": {

                /**
                 * Interface handler
                 *
                 * @type {gpf.interfaces.Interface}
                 * @private
                 */
                _iHandler: null,

                /**
                 * List of method calls
                 *
                 * @type {MethodCall[]}
                 * @private
                 */
                _calls: [],

                /**
                 * Callback to be used as event handler
                 *
                 * @type {Function}
                 * @private
                 */
                _callback: null,

                /**
                 * $catch callback
                 *
                 * @type {gpf.events.Handler}
                 * @private
                 */
                _catch: null,

                /**
                 * $finally callback
                 *
                 * @type {gpf.events.Handler}
                 * @private
                 */
                _finally: null,

                /**
                 * $finally event type
                 *
                 * @type {String}
                 * @private
                 */
                _finalEventType: "done",

                /**
                 * Event handler
                 *
                 * @param {gpf.Event} event
                 * @private
                 */
                _asyncResult: function (event) {
                    var
                        iHandler = this._iHandler,
                        calls;
                    if (event && event.type() === "error") {
                        if (this._catch) {
                            gpfFireEvent.call(iHandler, event, this._catch);
                        }
                        return;
                    }
                    calls = this._calls;
                    if (calls.length) {
                        calls.shift().apply(iHandler, this._callback);
                    } else if (this._finally) {
                        gpfFireEvent.call(iHandler, this._finalEventType, this._finally);
                    }
                },

                /**
                 * @type {Boolean}
                 * @private
                 */
                _needStart: true,

                /**
                 * When at least one method is called, this one makes sure the
                 * execution starts
                 *
                 * @private
                 */
                _start: function () {
                    if (this._needStart) {
                        this._needStart = false;
                        setTimeout(this._asyncResult.bind(this), 0);
                    }
                }

            },

            "+": {

                /**
                 * @param {Object} instance
                 * @constructor
                 */
                constructor: function (instance) {
                    this._iHandler = gpfI.query(instance,
                        this.constructor.interface);
                    this._calls = [];
                    this._callback = this._asyncResult.bind(this);
                },

                /**
                 * Configure the error handler
                 *
                 * @param {gpf.events.Handler} eventHandler
                 * @return {WrapInterface}
                 * @chainable
                 */
                $catch: function (eventHandler) {
                    this._catch = eventHandler;
                    this._start();
                    return this;
                },

                /**
                 * Configure the final handler
                 *
                 * @param {gpf.events.Handler} eventHandler
                 * @param {String}[eventType=done] eventType
                 * @return {WrapInterface}
                 * @chainable
                 */
                $finally: function (eventHandler, eventType) {
                    this._finally = eventHandler;
                    if (eventType) {
                        this._finalEventType = eventType;
                    }
                    this._start();
                    return this;
                }
            }

        });

    /**
     * Get or build the wrapper class for the given interface definition
     *
     * @param {Function} interfaceDef
     * @return {Function}
     */
    gpfI.wrap = function (interfaceDef) {
        var
            classDef = gpf.classDef(interfaceDef),
            result = _wrappers[classDef.uid()],
            base,
            nameOnly;
        if (undefined === result) {
            if (interfaceDef === gpfI.Interface) {
                result = WrapInterface;
            } else {
                base = gpfI.wrap(classDef.Base());
                nameOnly = classDef._name.split(".").pop();
                result = _gpfDefine("Wrap" + nameOnly, base, _buildMembers(interfaceDef));
            }
            _wrappers[classDef.uid()] = result;
        }
        return result;
    };

/*#ifndef(UMD)*/
}()); /* End of privacy scope */
/*#endif*/
<commit_msg>Prefix jsdoc extensions with gpf<commit_after>/**
 * @file Interface wrapper
 */
/*#ifndef(UMD)*/
(function () {/* Begin of privacy scope */
    "use strict";
    /*global _gpfAssert*/ // Assertion method
    /*global _gpfDefine*/ // Shortcut for gpf.define
/*#endif*/

    var
        gpfI = gpf.interfaces,
        gpfFireEvent = gpf.events.fire,
        _wrappers = {},

        /**
         * Build the synchronous member (no event)
         *
         * @param {String} name
         * @return {Function}
         * @gpf:closure
         * @private
         */
        _sync = function (name) {
            return function () {
                this._calls.push(new MethodCall(true, name, arguments, 0));
                this._start();
                return this;
            };
        },

        /**
         * Build the asynchronous member (i.e. event related)
         *
         * @param {String} name
         * @param {Number} length argument count in the function (used to deduce
         * where the event handler is)
         * @return {Function}
         * @gpf:closure
         * @private
         */
        _async = function (name, length) {
            return function () {
                this._calls.push(new MethodCall(false, name, arguments,
                    length));
                this._start();
                return this;
            };
        },

        _buildMembers = function (interfaceDef) {
            var
                result = {
                    "+": {},
                    "~": {
                        interface: interfaceDef
                    }
                },
                publicMembers = result.public,
                attributes = new gpf.attributes.Map(),
                prototype = interfaceDef.prototype,
                member,
                method;
            attributes.fillFromClassDef(gpf.classDef(interfaceDef));
            for (member in prototype) {
                if (prototype.hasOwnProperty(member)) {
                    method = prototype[member];
                    if ("constructor" === member) {
                        continue; // ignore
                    }
                    _gpfAssert("function" === typeof method, "Only methods");
                    if (attributes.member(member)
                        .has(gpf.attributes.ClassEventHandlerAttribute)) {
                        publicMembers[member] = _async(member, method.length);
                    } else {
                        publicMembers[member] = _sync(member);
                    }
                }
            }
            return result;
        },

        MethodCall = _gpfDefine("MethodCall", {

            "-": {

                /**
                 * Call is synchronous
                 *
                 * @type {Boolean}
                 * @private
                 */
                _synchronous: true,

                /**
                 * Method name
                 *
                 * @type {String}
                 * @private
                 */
                _name: "",

                /**
                 * Method arguments
                 *
                 * @type {Array}
                 * @private
                 */
                _args: [],

                /**
                 * When asynchronous, length provides the position of the event
                 * handler
                 *
                 * @type {Number}
                 * @private
                 */
                _length: 0

            },

            "+": {

                /**
                 *
                 * @param {Boolean} synchronous Call is synchronous
                 * @param {String} name Method name
                 * @param {Array} args Method arguments
                 * @param {Number} length When asynchronous, length provides the
                 * position of the event handler
                 */
                constructor: function (synchronous, name, args, length) {
                    this._synchronous = synchronous;
                    this._name = name;
                    this._args = args;
                    this._length = length;
                },

                /**
                 * Apply the call
                 *
                 * @param {Object} iHandler
                 * @param {Function} callback
                 */
                apply: function (iHandler, callback) {
                    var
                        args = this._args,
                        method = iHandler[this._name],
                        finalArgs,
                        count,
                        idx;
                    if (this._synchronous) {
                        method.apply(iHandler, args);
                        callback.call();
                    } else {
                        count = this._length;
                        finalArgs = new Array(count);
                        finalArgs[--count] = callback;
                        if (count > args.length) {
                            count = args.length;
                        }
                        for (idx = 0; idx < count; ++idx) {
                            finalArgs[idx] = args[idx];
                        }
                        method.apply(iHandler, finalArgs);
                    }
                }

            }

        }),

        WrapInterface = _gpfDefine("WrapInterface", {

            "-": {

                /**
                 * Interface handler
                 *
                 * @type {gpf.interfaces.Interface}
                 * @private
                 */
                _iHandler: null,

                /**
                 * List of method calls
                 *
                 * @type {MethodCall[]}
                 * @private
                 */
                _calls: [],

                /**
                 * Callback to be used as event handler
                 *
                 * @type {Function}
                 * @private
                 */
                _callback: null,

                /**
                 * $catch callback
                 *
                 * @type {gpf.events.Handler}
                 * @private
                 */
                _catch: null,

                /**
                 * $finally callback
                 *
                 * @type {gpf.events.Handler}
                 * @private
                 */
                _finally: null,

                /**
                 * $finally event type
                 *
                 * @type {String}
                 * @private
                 */
                _finalEventType: "done",

                /**
                 * Event handler
                 *
                 * @param {gpf.Event} event
                 * @private
                 */
                _asyncResult: function (event) {
                    var
                        iHandler = this._iHandler,
                        calls;
                    if (event && event.type() === "error") {
                        if (this._catch) {
                            gpfFireEvent.call(iHandler, event, this._catch);
                        }
                        return;
                    }
                    calls = this._calls;
                    if (calls.length) {
                        calls.shift().apply(iHandler, this._callback);
                    } else if (this._finally) {
                        gpfFireEvent.call(iHandler, this._finalEventType, this._finally);
                    }
                },

                /**
                 * @type {Boolean}
                 * @private
                 */
                _needStart: true,

                /**
                 * When at least one method is called, this one makes sure the
                 * execution starts
                 *
                 * @private
                 */
                _start: function () {
                    if (this._needStart) {
                        this._needStart = false;
                        setTimeout(this._asyncResult.bind(this), 0);
                    }
                }

            },

            "+": {

                /**
                 * @param {Object} instance
                 * @constructor
                 */
                constructor: function (instance) {
                    this._iHandler = gpfI.query(instance,
                        this.constructor.interface);
                    this._calls = [];
                    this._callback = this._asyncResult.bind(this);
                },

                /**
                 * Configure the error handler
                 *
                 * @param {gpf.events.Handler} eventHandler
                 * @gpf:chainable
                 */
                $catch: function (eventHandler) {
                    this._catch = eventHandler;
                    this._start();
                    return this;
                },

                /**
                 * Configure the final handler
                 *
                 * @param {gpf.events.Handler} eventHandler
                 * @param {String}[eventType=done] eventType
                 * @gpf:chainable
                 */
                $finally: function (eventHandler, eventType) {
                    this._finally = eventHandler;
                    if (eventType) {
                        this._finalEventType = eventType;
                    }
                    this._start();
                    return this;
                }
            }

        });

    /**
     * Get or build the wrapper class for the given interface definition
     *
     * @param {Function} interfaceDef
     * @return {Function}
     */
    gpfI.wrap = function (interfaceDef) {
        var
            classDef = gpf.classDef(interfaceDef),
            result = _wrappers[classDef.uid()],
            base,
            nameOnly;
        if (undefined === result) {
            if (interfaceDef === gpfI.Interface) {
                result = WrapInterface;
            } else {
                base = gpfI.wrap(classDef.Base());
                nameOnly = classDef._name.split(".").pop();
                result = _gpfDefine("Wrap" + nameOnly, base, _buildMembers(interfaceDef));
            }
            _wrappers[classDef.uid()] = result;
        }
        return result;
    };

/*#ifndef(UMD)*/
}()); /* End of privacy scope */
/*#endif*/
<|endoftext|>


<commit_before>'use strict';

import Promise from 'bluebird';
import Reflux from 'reflux';
import Firebase from 'firebase';

const baseRef = new Firebase('https://resplendent-fire-4810.firebaseio.com/');
const commentsRef = baseRef.child('comments');
const postsRef = baseRef.child('posts');
const usersRef = baseRef.child('users');

// used to create email hash for gravatar
const hash = require('crypto').createHash('md5');

const Actions = Reflux.createActions({
    // user actions
    'login': {},
    'logout': {},
    'register': {},
    'createProfile': {},
    'updateProfile': {},
    // post actions
    'upvotePost': {},
    'downvotePost': {},
    'submitPost': { asyncResult: true },
    'deletePost': {},
    'setSortBy': {},
    // comment actions
    'upvoteComment': {},
    'downvoteComment': {},
    'updateCommentCount': {},
    'addComment': {},
    'deleteComment': {},
    // firebase actions
    'watchPost': {},
    'watchPosts': {},
    'watchProfile': {},
    'stopWatchingPost': {},
    'stopWatchingPosts': {},
    'stopWatchingProfile': {},
    // error actions
    'loginError': {},
    'clearError': {},
    // ui actions
    'showModal': {},
    'hideModal': {},
    'goToPost': {}
});

/* User Actions
=============================== */

Actions.login.listen(function(loginData) {
    baseRef.authWithPassword(loginData, error => (
        error && Actions.loginError(error.code)
    ));
});

// this registration code could probably be cleaned up some

function checkForUsername(newName) {
    // checks if username is blank/is already taken
    return new Promise(function(resolve, reject) {
        if (!newName) {
            return reject({ code: 'NO_USERNAME' });
        }

        // check if taken
        usersRef.orderByChild('username').equalTo(newName).once('value', user => (
            user.val()
                ? reject({ code: 'USERNAME_TAKEN' })
                : resolve()
        ));
    });
}

function createUser(username, loginData) {

    let profile = {
        username: username,
        md5hash: hash.update(loginData.email).digest('hex'),
        upvoted: {}
    };

    baseRef.createUser(loginData, function(error, userData) {
        if (error) {
            // email taken, other login errors
            return Actions.loginError(error.code);
        }

        // user successfully created
        // now create user profile and log them in
        usersRef.child(userData.uid).set(profile, err => err || Actions.login(loginData));
    });
}

Actions.register.listen(function(username, loginData) {
    checkForUsername(username)
        // username is available
        .then(() => createUser(username, loginData))
        // username is taken
        .catch(error => Actions.loginError(error.code));
});

/* Post Actions
=============================== */

Actions.submitPost.listen(function(post) {
    let newPostRef = postsRef.push(post, error => (
        error ? this.failed(error) : this.completed(newPostRef.key())
    ));
});

Actions.deletePost.listen(function(postId) {
    postsRef.child(postId).transaction(postObj => ({
        isDeleted: true,
        // keep comment count
        commentCount: postObj.commentCount
    }));
});

/*
    I debated for a while here about whether it's okay to trust these
    callbacks to keep things in sync. I looked at Firebase Util (still very
    beta as of June 2015) and Firebase Multi Write but decided that the extra
    dependencies were probably overkill for this project. If you need more
    guarantees that the data will stay in sync, check them out:

    https://github.com/firebase/firebase-util
    https://github.com/katowulf/firebase-multi-write
*/

function updatePostUpvotes(postId, n) {
    postsRef.child(postId + '/upvotes').transaction(curr => (curr || 0) + n);
}

Actions.upvotePost.listen(function(userId, postId) {
    // register postId in user's profile
    usersRef.child(userId + '/upvoted/' + postId).set(true, function(error) {
        if (error) { return; }
        // increment post's upvotes
        updatePostUpvotes(postId, 1);
    });
});

Actions.downvotePost.listen(function(userId, postId) {
    // remove upvote in user's profile
    usersRef.child(userId + '/upvoted/' + postId).remove(function(error) {
        if (error) { return; }
        // increment post's upvotes
        updatePostUpvotes(postId, -1);
    });
});

/* Comment Actions
=============================== */

function updateCommentUpvotes(commentId, n) {
    commentsRef.child(commentId + '/upvotes').transaction(curr => (curr || 0) + n);
}

Actions.upvoteComment.listen(function(userId, commentId) {
    // register upvote in user's profile
    usersRef.child(userId + '/upvoted/' + commentId).set(true, function(error) {
        if (error) { return; }
        updateCommentUpvotes(commentId, 1);
    });
});

Actions.downvoteComment.listen(function(userId, commentId) {
    usersRef.child(userId + '/upvoted/' + commentId).remove(function(error) {
        if (error) { return; }
        updateCommentUpvotes(commentId, -1);
    });
});

function updateCommentCount(postId, n) {
    // updates comment count on post
    postsRef.child(postId + '/commentCount').transaction(curr => (curr || 0) + n);
}

Actions.addComment.listen(function(comment) {
    commentsRef.push(comment, function(error) {
        if (error) { return; }
        updateCommentCount(comment.postId, 1);
    });
});

Actions.deleteComment.listen(function(commentId, postId) {
    commentsRef.child(commentId).remove(function(error) {
        if (error) { return; }
        updateCommentCount(postId, -1);
    });
});

Actions.showModal.preEmit = function(modalType, errorType) {
    if (errorType) {
        Actions.loginError(errorType);
    } else {
        Actions.clearError();
    }
};

export default Actions;
<commit_msg>remove unnecessary commentCount from deleted posts<commit_after>'use strict';

import Promise from 'bluebird';
import Reflux from 'reflux';
import Firebase from 'firebase';

const baseRef = new Firebase('https://resplendent-fire-4810.firebaseio.com/');
const commentsRef = baseRef.child('comments');
const postsRef = baseRef.child('posts');
const usersRef = baseRef.child('users');

// used to create email hash for gravatar
const hash = require('crypto').createHash('md5');

const Actions = Reflux.createActions({
    // user actions
    'login': {},
    'logout': {},
    'register': {},
    'createProfile': {},
    'updateProfile': {},
    // post actions
    'upvotePost': {},
    'downvotePost': {},
    'submitPost': { asyncResult: true },
    'deletePost': {},
    'setSortBy': {},
    // comment actions
    'upvoteComment': {},
    'downvoteComment': {},
    'updateCommentCount': {},
    'addComment': {},
    'deleteComment': {},
    // firebase actions
    'watchPost': {},
    'watchPosts': {},
    'watchProfile': {},
    'stopWatchingPost': {},
    'stopWatchingPosts': {},
    'stopWatchingProfile': {},
    // error actions
    'loginError': {},
    'clearError': {},
    // ui actions
    'showModal': {},
    'hideModal': {},
    'goToPost': {}
});

/* User Actions
=============================== */

Actions.login.listen(function(loginData) {
    baseRef.authWithPassword(loginData, error => (
        error && Actions.loginError(error.code)
    ));
});

// this registration code could probably be cleaned up some

function checkForUsername(newName) {
    // checks if username is blank/is already taken
    return new Promise(function(resolve, reject) {
        if (!newName) {
            return reject({ code: 'NO_USERNAME' });
        }

        // check if taken
        usersRef.orderByChild('username').equalTo(newName).once('value', user => (
            user.val()
                ? reject({ code: 'USERNAME_TAKEN' })
                : resolve()
        ));
    });
}

function createUser(username, loginData) {

    let profile = {
        username: username,
        md5hash: hash.update(loginData.email).digest('hex'),
        upvoted: {}
    };

    baseRef.createUser(loginData, function(error, userData) {
        if (error) {
            // email taken, other login errors
            return Actions.loginError(error.code);
        }

        // user successfully created
        // now create user profile and log them in
        usersRef.child(userData.uid).set(profile, err => err || Actions.login(loginData));
    });
}

Actions.register.listen(function(username, loginData) {
    checkForUsername(username)
        // username is available
        .then(() => createUser(username, loginData))
        // username is taken
        .catch(error => Actions.loginError(error.code));
});

/* Post Actions
=============================== */

Actions.submitPost.listen(function(post) {
    let newPostRef = postsRef.push(post, error => (
        error ? this.failed(error) : this.completed(newPostRef.key())
    ));
});

Actions.deletePost.listen(function(postId) {
    postsRef.child(postId).set({
        isDeleted: true
    });
});

/*
    I debated for a while here about whether it's okay to trust these
    callbacks to keep things in sync. I looked at Firebase Util (still very
    beta as of June 2015) and Firebase Multi Write but decided that the extra
    dependencies were probably overkill for this project. If you need more
    guarantees that the data will stay in sync, check them out:

    https://github.com/firebase/firebase-util
    https://github.com/katowulf/firebase-multi-write
*/

function updatePostUpvotes(postId, n) {
    postsRef.child(postId + '/upvotes').transaction(curr => (curr || 0) + n);
}

Actions.upvotePost.listen(function(userId, postId) {
    // register postId in user's profile
    usersRef.child(userId + '/upvoted/' + postId).set(true, function(error) {
        if (error) { return; }
        // increment post's upvotes
        updatePostUpvotes(postId, 1);
    });
});

Actions.downvotePost.listen(function(userId, postId) {
    // remove upvote in user's profile
    usersRef.child(userId + '/upvoted/' + postId).remove(function(error) {
        if (error) { return; }
        // increment post's upvotes
        updatePostUpvotes(postId, -1);
    });
});

/* Comment Actions
=============================== */

function updateCommentUpvotes(commentId, n) {
    commentsRef.child(commentId + '/upvotes').transaction(curr => (curr || 0) + n);
}

Actions.upvoteComment.listen(function(userId, commentId) {
    // register upvote in user's profile
    usersRef.child(userId + '/upvoted/' + commentId).set(true, function(error) {
        if (error) { return; }
        updateCommentUpvotes(commentId, 1);
    });
});

Actions.downvoteComment.listen(function(userId, commentId) {
    usersRef.child(userId + '/upvoted/' + commentId).remove(function(error) {
        if (error) { return; }
        updateCommentUpvotes(commentId, -1);
    });
});

function updateCommentCount(postId, n) {
    // updates comment count on post
    postsRef.child(postId + '/commentCount').transaction(curr => (curr || 0) + n);
}

Actions.addComment.listen(function(comment) {
    commentsRef.push(comment, function(error) {
        if (error) { return; }
        updateCommentCount(comment.postId, 1);
    });
});

Actions.deleteComment.listen(function(commentId, postId) {
    commentsRef.child(commentId).remove(function(error) {
        if (error) { return; }
        updateCommentCount(postId, -1);
    });
});

Actions.showModal.preEmit = function(modalType, errorType) {
    if (errorType) {
        Actions.loginError(errorType);
    } else {
        Actions.clearError();
    }
};

export default Actions;
<|endoftext|>


<commit_before>// (C) Copyright 2014-2016 Hewlett Packard Enterprise Development LP

import React, { Component, PropTypes } from 'react';
import classnames from 'classnames';
import CSSClassnames from '../utils/CSSClassnames';
import Props from '../utils/Props';
import Box from './Box';
import Tile from './Tile';
import Heading from './Heading';
import Paragraph from './Paragraph';
import Anchor from './Anchor';
import Layer from './Layer';
import Video from './Video';
import WatchIcon from './icons/base/Watch';

const CLASS_ROOT = CSSClassnames.CARD;

export default class Card extends Component {
  constructor (props) {
    super(props);
    this._onClick = this._onClick.bind(this);
    this.state = {
      activeVideo: false
    };
  }

  _onClick (event) {
    const { video } = this.props;

    if (video) {
      event.preventDefault();
      this.setState({ activeVideo : !this.state.activeVideo });
    }
  }

  _renderLink () {
    const { link } = this.props;

    if (link) {
      return (
        <Box pad={{vertical: "small"}}>
          {link}
        </Box>
      );
    }

    return null;
  }

  _renderVideo () {
    const { video } = this.props;
    const { activeVideo } = this.state;
    let layerContent;
    let videoLayer;

    if (video && activeVideo) {
      if (video.source) {
        layerContent = (
          <Video>
            <source src={video.source} type={`video/${video.type}`}/>
          </Video>
        );
      } else {
        layerContent = video;
      }

      videoLayer = (
        <Layer onClose={this._onClick} closer={true} flush={true}>
          {layerContent}
        </Layer>
      );
    }

    return videoLayer;
  }

  _renderParagraph (contents, textSize, type) {
    if (typeof contents === 'string') {
      return (
        <Paragraph
          className={`${CLASS_ROOT}__${type}`}
          size={textSize}
          margin="none"
        >
          {contents}
        </Paragraph>
      );
    } else if (Array.isArray(contents)) {
      return contents.map((content, index) => (
        <Paragraph
          key={`${type}_${index}`}
          className={`${CLASS_ROOT}__${type}`}
          size={textSize}
          margin="none"
        >
          {content}
        </Paragraph>
      ));
    }
    return null;
  }

  render () {
    const { children, thumbnail, description, heading, headingStrong, label,
      onClick, video, direction, reverse, pad, className, textSize, paragraph,
      transparent } = this.props;
    const tileProps = Props.pick(this.props, Object.keys(Tile.propTypes));
    delete tileProps.onClick;
    delete tileProps.pad;

    const classes = classnames(
      CLASS_ROOT,
      {
        [`${CLASS_ROOT}--direction-${direction}`]: direction,
        [`${CLASS_ROOT}--selectable`]: (onClick || video),
        [`${CLASS_ROOT}--${textSize}`]: textSize
      },
      className
    );

    let labelTag;
    let headingTag;
    let paragraphSize;
    let descriptionSize;
    if (textSize === 'xlarge') {
      labelTag = 'h3';
      headingTag = 'h1';
      paragraphSize = 'xlarge';
      descriptionSize = 'large';
    } else if (textSize === 'large') {
      labelTag = 'h4';
      headingTag = 'h1';
      paragraphSize = 'xlarge';
      descriptionSize = 'large';
    } else if (textSize === 'medium') {
      labelTag = 'h4';
      headingTag = 'h2';
      paragraphSize = 'large';
      descriptionSize = 'medium';
    } else {
      labelTag = 'h5';
      headingTag = 'h3';
      paragraphSize = 'medium';
      descriptionSize = 'small';
    }

    let onCardClick = onClick;
    if (!onCardClick && video) {
      onCardClick = this._onClick;
    }

    const contentContainer = (
      <Box className={`${CLASS_ROOT}__content`} pad="medium">
        {label &&
          <Heading className={`${CLASS_ROOT}__label`}
            tag={labelTag} margin="none" uppercase={true}>
            {label}
          </Heading>
        }
        {heading &&
          <Heading className={`${CLASS_ROOT}__heading`}
            tag={headingTag} strong={headingStrong} margin="none">
            {heading}
          </Heading>
        }
        {this._renderParagraph(paragraph, paragraphSize, 'paragraph')}
        {this._renderParagraph(description, descriptionSize, 'description')}
        {children}
        {this._renderLink()}
      </Box>
    );

    let thumbnailContainer;
    if (thumbnail) {
      thumbnailContainer = (
        <Box className={`${CLASS_ROOT}__thumbnail`}
          backgroundImage={`url(${thumbnail})`}
          justify="center" align="center">
          {(video) ? <Anchor icon={<WatchIcon size="xlarge" />} /> : null}
        </Box>
      );
    }

    let first = thumbnailContainer;
    let second = contentContainer;
    let cardJustify;

    if (reverse) {
      first = contentContainer;
      second = thumbnailContainer;
      // align thumbnail to bottom of card for bottom cardPlacement
      cardJustify = 'between';
    }

    let cardPad = 'small';
    let cardFull;
    if (direction === 'row') {
      cardPad = {vertical: 'small'};
      cardFull = 'horizontal';
    }

    return (
      <Tile className={classes} onClick={onCardClick}
        pad={pad || cardPad} {...tileProps}>
        <Box className="flex" direction={direction} justify={cardJustify}
          full={cardFull} colorIndex={!transparent ? 'light-1' : ''}>
          {first}
          {second}
          {this._renderVideo()}
        </Box>
      </Tile>
    );
  }
};

Card.propTypes = {
  thumbnail: PropTypes.string,
  textSize: PropTypes.oneOf(['small', 'medium', 'large', 'xlarge']),
  label: PropTypes.string,
  heading: PropTypes.string,
  headingStrong: PropTypes.bool,
  paragraph: PropTypes.string,
  description: PropTypes.string,
  video: PropTypes.oneOfType([
    PropTypes.shape({
      source: PropTypes.string.isRequired,
      type: PropTypes.string
    }),
    PropTypes.element
  ]),
  reverse: PropTypes.bool,
  transparent: PropTypes.bool,
  link: PropTypes.string,
  linkText: PropTypes.string,
  linkIcon: PropTypes.element,
  onClick: PropTypes.func,
  ...Tile.propTypes
};

Card.defaultProps = {
  direction: 'column',
  textSize: 'medium',
  headingStrong: true,
  linkText: 'Learn More'
};
<commit_msg>Move text tags to top of file as obj & rename paragraph & description<commit_after>// (C) Copyright 2014-2016 Hewlett Packard Enterprise Development LP

import React, { Component, PropTypes } from 'react';
import classnames from 'classnames';
import CSSClassnames from '../utils/CSSClassnames';
import Props from '../utils/Props';
import Box from './Box';
import Tile from './Tile';
import Heading from './Heading';
import Paragraph from './Paragraph';
import Anchor from './Anchor';
import Layer from './Layer';
import Video from './Video';
import WatchIcon from './icons/base/Watch';

const CLASS_ROOT = CSSClassnames.CARD;
const TEXT_TAGS = {
  xlarge: {
    label: 'h3',
    heading: 'h1',
    summary: 'xlarge',
    details: 'large'
  },
  large: {
    label: 'h4',
    heading: 'h1',
    summary: 'xlarge',
    details: 'large'
  },
  medium: {
    label: 'h4',
    heading: 'h2',
    summary: 'large',
    details: 'medium'
  },
  small: {
    label: 'h5',
    heading: 'h3',
    summary: 'medium',
    details: 'small'
  }
};

export default class Card extends Component {
  constructor (props) {
    super(props);
    this._onClick = this._onClick.bind(this);
    this.state = {
      activeVideo: false
    };
  }

  _onClick (event) {
    const { video } = this.props;

    if (video) {
      event.preventDefault();
      this.setState({ activeVideo : !this.state.activeVideo });
    }
  }

  _renderLink () {
    const { link } = this.props;

    if (link) {
      return (
        <Box pad={{vertical: "small"}}>
          {link}
        </Box>
      );
    }

    return null;
  }

  _renderVideo () {
    const { video } = this.props;
    const { activeVideo } = this.state;
    let layerContent;
    let videoLayer;

    if (video && activeVideo) {
      if (video.source) {
        layerContent = (
          <Video>
            <source src={video.source} type={`video/${video.type}`}/>
          </Video>
        );
      } else {
        layerContent = video;
      }

      videoLayer = (
        <Layer onClose={this._onClick} closer={true} flush={true}>
          {layerContent}
        </Layer>
      );
    }

    return videoLayer;
  }

  _renderParagraph (contents, textSize, type) {
    if (typeof contents === 'string') {
      return (
        <Paragraph
          className={`${CLASS_ROOT}__${type}`}
          size={textSize}
          margin="none"
        >
          {contents}
        </Paragraph>
      );
    } else if (Array.isArray(contents)) {
      return contents.map((content, index) => (
        <Paragraph
          key={`${type}_${index}`}
          className={`${CLASS_ROOT}__${type}`}
          size={textSize}
          margin="none"
        >
          {content}
        </Paragraph>
      ));
    }
    return null;
  }

  render () {
    const { children, thumbnail, details, heading, headingStrong, label,
      onClick, video, direction, reverse, pad, className, textSize, summary,
      transparent } = this.props;
    const tileProps = Props.pick(this.props, Object.keys(Tile.propTypes));
    delete tileProps.onClick;
    delete tileProps.pad;

    const classes = classnames(
      CLASS_ROOT,
      {
        [`${CLASS_ROOT}--direction-${direction}`]: direction,
        [`${CLASS_ROOT}--selectable`]: (onClick || video),
        [`${CLASS_ROOT}--${textSize}`]: textSize
      },
      className
    );

    let onCardClick = onClick;
    if (!onCardClick && video) {
      onCardClick = this._onClick;
    }

    const tag = TEXT_TAGS[textSize];

    const contentContainer = (
      <Box className={`${CLASS_ROOT}__content`} pad="medium">
        {label &&
          <Heading className={`${CLASS_ROOT}__label`}
            tag={tag.label} margin="none" uppercase={true}>
            {label}
          </Heading>
        }
        {heading &&
          <Heading className={`${CLASS_ROOT}__heading`}
            tag={tag.heading} strong={headingStrong} margin="none">
            {heading}
          </Heading>
        }
        {this._renderParagraph(summary, tag.summary, 'summary')}
        {this._renderParagraph(details, tag.details, 'details')}
        {children}
        {this._renderLink()}
      </Box>
    );

    let thumbnailContainer;
    if (thumbnail) {
      thumbnailContainer = (
        <Box className={`${CLASS_ROOT}__thumbnail`}
          backgroundImage={`url(${thumbnail})`}
          justify="center" align="center">
          {(video) ? <Anchor icon={<WatchIcon size="xlarge" />} /> : null}
        </Box>
      );
    }

    let first = thumbnailContainer;
    let second = contentContainer;
    let cardJustify;

    if (reverse) {
      first = contentContainer;
      second = thumbnailContainer;
      // align thumbnail to bottom of card for bottom cardPlacement
      cardJustify = 'between';
    }

    let cardPad = 'small';
    let cardFull;
    if (direction === 'row') {
      cardPad = {vertical: 'small'};
      cardFull = 'horizontal';
    }

    return (
      <Tile className={classes} onClick={onCardClick}
        pad={pad || cardPad} {...tileProps}>
        <Box className="flex" direction={direction} justify={cardJustify}
          full={cardFull} colorIndex={!transparent ? 'light-1' : ''}>
          {first}
          {second}
          {this._renderVideo()}
        </Box>
      </Tile>
    );
  }
};

Card.propTypes = {
  thumbnail: PropTypes.string,
  textSize: PropTypes.oneOf(['small', 'medium', 'large', 'xlarge']),
  label: PropTypes.string,
  heading: PropTypes.string,
  headingStrong: PropTypes.bool,
  summary: PropTypes.node,
  details: PropTypes.node,
  video: PropTypes.oneOfType([
    PropTypes.shape({
      source: PropTypes.string.isRequired,
      type: PropTypes.string
    }),
    PropTypes.element
  ]),
  reverse: PropTypes.bool,
  transparent: PropTypes.bool,
  link: PropTypes.string,
  linkText: PropTypes.string,
  linkIcon: PropTypes.element,
  onClick: PropTypes.func,
  ...Tile.propTypes
};

Card.defaultProps = {
  direction: 'column',
  textSize: 'medium',
  headingStrong: true,
  linkText: 'Learn More'
};
<|endoftext|>


<commit_before>(function(addon) {

    var component;

    if (window.UIkit) {
        component = addon(UIkit);
    }

    if (typeof define == "function" && define.amd) {
        define("uikit-grid", ["uikit"], function(){
            return component || addon(UIkit);
        });
    }

})(function(UI){

    "use strict";

    UI.component('grid', {

        defaults: {
            colwidth  : 'auto',
            animation : true,
            duration  : 300,
            gutter    : 0,
            controls  : false
        },

        boot:  function() {

            // init code
            UI.ready(function(context) {

                UI.$('[data-uk-grid]', context).each(function(){

                    var ele = UI.$(this);

                    if(!ele.data("grid")) {
                        var plugin = UI.grid(ele, UI.Utils.options(ele.attr('data-uk-grid')));
                    }
                });
            });
        },

        init: function() {

            var $this = this;

            // make sure parent element has the right position property
            this.element.css({'position': 'relative'});

            if (this.options.controls) {

                var controls  = UI.$(this.options.controls),
                    activeCls = 'uk-active';

                // filter
                controls.on('click', '[data-uk-filter]', function(e){
                    e.preventDefault();
                    $this.filter(UI.$(this).data('ukFilter'));

                    controls.find('[data-uk-filter]').removeClass(activeCls).filter(this).addClass(activeCls);
                });

                // sort
                controls.on('click', '[data-uk-sort]', function(e){
                    e.preventDefault();

                    var cmd = UI.$(this).attr('data-uk-sort').split(':');

                    $this.sort(cmd[0], cmd[1]);

                    controls.find('[data-uk-sort]').removeClass(activeCls).filter(this).addClass(activeCls);
                });
            }

            UI.$win.on('load resize orientationchange', UI.Utils.debounce(function(){
                this.updateLayout();
            }.bind(this), 100));

            this.updateLayout();

            this.on('display.uk.check', function(){
                if ($this.element.is(":visible"))  $this.updateLayout();
            });

            UI.$html.on("changed.uk.dom", function(e) {
                $this.updateLayout();
            });
        },

        _prepareElements: function() {

            var children = this.element.children(':not([data-grid-prepared])'), css;

            // exit if no already prepared elements found
            if (!children.length) {
                return;
            }

            css = {
                'position'   : 'absolute',
                'box-sizing' : 'border-box',
                'width'      : this.options.colwidth == 'auto' ? '' : this.options.colwidth
            };

            if (this.options.gutter) {
                css['padding-left']   = this.options.gutter;
                css['padding-bottom'] = this.options.gutter;

                this.element.css('margin-left', this.options.gutter * -1);
            }

            children.attr('data-grid-prepared', 'true').css(css);
        },

        updateLayout: function(elements) {

            this._prepareElements();

            elements = elements || this.element.children(':visible');

            var $this     = this,
                gutter    = this.options.gutter,
                children  = elements,
                maxwidth  = this.element.width() + (2*gutter) + 2,
                left      = 0,
                top       = 0,
                positions = [],

                item, width, height, pos, aX, aY, i, z, max, size;

            this.trigger('beforeupdate.uk.grid', [children]);

            children.each(function(index){

                size   = getElementSize(this);

                item   = UI.$(this);
                width  = size.outerWidth;
                height = size.outerHeight;
                left   = 0;
                top    = 0;

                for (i=0,max=positions.length;i<max;i++) {

                    pos = positions[i];

                    if (left <= pos.aX) { left = pos.aX; }
                    if (maxwidth < (left + width)) { left = 0; }
                    if (top <= pos.aY) { top = pos.aY; }
                }

                positions.push({
                    "ele"    : item,
                    "top"    : top,
                    "left"   : left,
                    "width"  : width,
                    "height" : height,
                    "aY"     : (top  + height),
                    "aX"     : (left + width)
                });
            });

            var posPrev, maxHeight = 0;

            // fix top
            for (i=0,max=positions.length;i<max;i++) {

                pos = positions[i];
                top = 0;

                for (z=0;z<i;z++) {

                    posPrev = positions[z];

                    // (posPrev.left + 1) fixex 1px bug when using % based widths
                    if (pos.left < posPrev.aX && (posPrev.left +1) < pos.aX) {
                        top = posPrev.aY;
                    }
                }

                pos.top = top;
                pos.aY  = top + pos.height;

                maxHeight = Math.max(maxHeight, pos.aY);
            }

            maxHeight = maxHeight - gutter;

            if (this.options.animation) {

                this.element.stop().animate({'height': maxHeight}, 100);

                positions.forEach(function(pos){
                    pos.ele.stop().animate({"top": pos.top, "left": pos.left, opacity: 1}, this.options.duration);
                }.bind(this));

            } else {

                this.element.css('height', maxHeight);

                positions.forEach(function(pos){
                    pos.ele.css({"top": pos.top, "left": pos.left, opacity: 1});
                }.bind(this));
            }

            // make sure to trigger possible scrollpies etc.
            setTimeout(function() {
                UI.$doc.trigger('scrolling.uk.document');
            }, 2 * this.options.duration * (this.options.animation ? 1:0));

            this.trigger('afterupdate.uk.grid', [children]);
        },

        filter: function(filter) {

            filter = filter || [];

            if (typeof(filter) === 'string') {
                filter = filter.split(/,/).map(function(item){ return item.trim(); });
            }

            var $this = this, children = this.element.children(), elements = {"visible": [], "hidden": []}, visible, hidden;

            children.each(function(index){

                var ele = UI.$(this), f = ele.attr('data-uk-filter'), infilter = filter.length ? false : true;

                if (f) {

                    f = f.split(/,/).map(function(item){ return item.trim(); });

                    filter.forEach(function(item){
                        if (f.indexOf(item) > -1) infilter = true;
                    });
                }

                elements[infilter ? "visible":"hidden"].push(ele);
            });

            // convert to jQuery collections
            elements.hidden  = UI.$(elements.hidden).map(function () {return this[0];});
            elements.visible = UI.$(elements.visible).map(function () {return this[0];});

            elements.hidden.filter(':visible').fadeOut(this.options.duration);

            elements.visible.filter(':hidden').css('opacity', 0).show();

            $this.updateLayout(elements.visible);
        },

        sort: function(by, order){

            order = order || 1;

            // covert from string (asc|desc) to number
            if (typeof(order) === 'string') {
                order = order.toLowerCase() == 'desc' ? -1 : 1;
            }

            var elements = this.element.children();

            elements.sort(function(a, b){

                a = UI.$(a);
                b = UI.$(b);

                return (b.data(by) || '') < (a.data(by) || '') ? order : (order*-1);

            }).appendTo(this.element);

            this.updateLayout(elements.filter(':visible'));
        }
    });

    /*!
    * getSize v1.2.2
    * measure size of elements
    * MIT license
    * https://github.com/desandro/get-size
    */
    var _getSize = (function(){

        var prefixes = 'Webkit Moz ms Ms O'.split(' ');
        var docElemStyle = document.documentElement.style;

        function getStyleProperty( propName ) {
            if ( !propName ) {
                return;
            }

            // test standard property first
            if ( typeof docElemStyle[ propName ] === 'string' ) {
                return propName;
            }

            // capitalize
            propName = propName.charAt(0).toUpperCase() + propName.slice(1);

            // test vendor specific properties
            var prefixed;
            for ( var i=0, len = prefixes.length; i < len; i++ ) {
                prefixed = prefixes[i] + propName;
                if ( typeof docElemStyle[ prefixed ] === 'string' ) {
                    return prefixed;
                }
            }
        }

        // -------------------------- helpers -------------------------- //

        // get a number from a string, not a percentage
        function getStyleSize( value ) {
            var num = parseFloat( value );
            // not a percent like '100%', and a number
            var isValid = value.indexOf('%') === -1 && !isNaN( num );
            return isValid && num;
        }

        function noop() {}

        var logError = typeof console === 'undefined' ? noop : function( message ) {
            console.error( message );
        };

        // -------------------------- measurements -------------------------- //

        var measurements = [
            'paddingLeft',
            'paddingRight',
            'paddingTop',
            'paddingBottom',
            'marginLeft',
            'marginRight',
            'marginTop',
            'marginBottom',
            'borderLeftWidth',
            'borderRightWidth',
            'borderTopWidth',
            'borderBottomWidth'
        ];

        function getZeroSize() {
            var size = {
                width: 0,
                height: 0,
                innerWidth: 0,
                innerHeight: 0,
                outerWidth: 0,
                outerHeight: 0
            };
            for ( var i=0, len = measurements.length; i < len; i++ ) {
                var measurement = measurements[i];
                size[ measurement ] = 0;
            }
            return size;
        }

        // -------------------------- setup -------------------------- //

        var isSetup = false;
        var getStyle, boxSizingProp, isBoxSizeOuter;

        /**
        * setup vars and functions
        * do it on initial getSize(), rather than on script load
        * For Firefox bug https://bugzilla.mozilla.org/show_bug.cgi?id=548397
        */
        function setup() {
            // setup once
            if ( isSetup ) {
                return;
            }
            isSetup = true;

            var getComputedStyle = window.getComputedStyle;
            getStyle = ( function() {
                var getStyleFn = getComputedStyle ?
                function( elem ) {
                    return getComputedStyle( elem, null );
                } :
                function( elem ) {
                    return elem.currentStyle;
                };

                return function getStyle( elem ) {
                    var style = getStyleFn( elem );
                    if ( !style ) {
                        logError( 'Style returned ' + style +
                        '. Are you running this code in a hidden iframe on Firefox? ' +
                        'See http://bit.ly/getsizebug1' );
                    }
                    return style;
                };
            })();

            // -------------------------- box sizing -------------------------- //

            boxSizingProp = getStyleProperty('boxSizing');

            /**
            * WebKit measures the outer-width on style.width on border-box elems
            * IE & Firefox measures the inner-width
            */
            if ( boxSizingProp ) {
                var div = document.createElement('div');
                div.style.width = '200px';
                div.style.padding = '1px 2px 3px 4px';
                div.style.borderStyle = 'solid';
                div.style.borderWidth = '1px 2px 3px 4px';
                div.style[ boxSizingProp ] = 'border-box';

                var body = document.body || document.documentElement;
                body.appendChild( div );
                var style = getStyle( div );

                isBoxSizeOuter = getStyleSize( style.width ) === 200;
                body.removeChild( div );
            }

        }

        // -------------------------- getSize -------------------------- //

        function getSize( elem ) {
            setup();

            // use querySeletor if elem is string
            if ( typeof elem === 'string' ) {
                elem = document.querySelector( elem );
            }

            // do not proceed on non-objects
            if ( !elem || typeof elem !== 'object' || !elem.nodeType ) {
                return;
            }

            var style = getStyle( elem );

            // if hidden, everything is 0
            if ( style.display === 'none' ) {
                return getZeroSize();
            }

            var size = {};
            size.width = elem.offsetWidth;
            size.height = elem.offsetHeight;

            var isBorderBox = size.isBorderBox = !!( boxSizingProp &&
                style[ boxSizingProp ] && style[ boxSizingProp ] === 'border-box' );

            // get all measurements
            for ( var i=0, len = measurements.length; i < len; i++ ) {
                var measurement = measurements[i];
                var value = style[ measurement ];

                var num = parseFloat( value );
                // any 'auto', 'medium' value will be 0
                size[ measurement ] = !isNaN( num ) ? num : 0;
            }

            var paddingWidth = size.paddingLeft + size.paddingRight;
            var paddingHeight = size.paddingTop + size.paddingBottom;
            var marginWidth = size.marginLeft + size.marginRight;
            var marginHeight = size.marginTop + size.marginBottom;
            var borderWidth = size.borderLeftWidth + size.borderRightWidth;
            var borderHeight = size.borderTopWidth + size.borderBottomWidth;

            var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter;

            // overwrite width and height if we can get it from style
            var styleWidth = getStyleSize( style.width );
            if ( styleWidth !== false ) {
                size.width = styleWidth +
                // add padding and border unless it's already including it
                ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth );
            }

            var styleHeight = getStyleSize( style.height );
            if ( styleHeight !== false ) {
                size.height = styleHeight +
                // add padding and border unless it's already including it
                ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight );
            }

            size.innerWidth = size.width - ( paddingWidth + borderWidth );
            size.innerHeight = size.height - ( paddingHeight + borderHeight );

            size.outerWidth = size.width + marginWidth;
            size.outerHeight = size.height + marginHeight;

            return size;
        }

        return getSize;

    })();

    function getElementSize(ele) {
        return _getSize(ele);
    }
});
<commit_msg>added aria support to dynamic grid<commit_after>(function(addon) {

    var component;

    if (window.UIkit) {
        component = addon(UIkit);
    }

    if (typeof define == "function" && define.amd) {
        define("uikit-grid", ["uikit"], function(){
            return component || addon(UIkit);
        });
    }

})(function(UI){

    "use strict";

    UI.component('grid', {

        defaults: {
            colwidth  : 'auto',
            animation : true,
            duration  : 300,
            gutter    : 0,
            controls  : false
        },

        boot:  function() {

            // init code
            UI.ready(function(context) {

                UI.$('[data-uk-grid]', context).each(function(){

                    var ele = UI.$(this);

                    if(!ele.data("grid")) {
                        var plugin = UI.grid(ele, UI.Utils.options(ele.attr('data-uk-grid')));
                    }
                });
            });
        },

        init: function() {

            var $this = this;

            // make sure parent element has the right position property
            this.element.css({'position': 'relative'});

            if (this.options.controls) {

                var controls  = UI.$(this.options.controls),
                    activeCls = 'uk-active';

                // filter
                controls.on('click', '[data-uk-filter]', function(e){
                    e.preventDefault();
                    $this.filter(UI.$(this).data('ukFilter'));

                    controls.find('[data-uk-filter]').removeClass(activeCls).filter(this).addClass(activeCls);
                });

                // sort
                controls.on('click', '[data-uk-sort]', function(e){
                    e.preventDefault();

                    var cmd = UI.$(this).attr('data-uk-sort').split(':');

                    $this.sort(cmd[0], cmd[1]);

                    controls.find('[data-uk-sort]').removeClass(activeCls).filter(this).addClass(activeCls);
                });
            }

            UI.$win.on('load resize orientationchange', UI.Utils.debounce(function(){
                this.updateLayout();
            }.bind(this), 100));

            this.updateLayout();

            this.on('display.uk.check', function(){
                if ($this.element.is(":visible"))  $this.updateLayout();
            });

            UI.$html.on("changed.uk.dom", function(e) {
                $this.updateLayout();
            });
        },

        _prepareElements: function() {

            var children = this.element.children(':not([data-grid-prepared])'), css;

            // exit if no already prepared elements found
            if (!children.length) {
                return;
            }

            css = {
                'position'   : 'absolute',
                'box-sizing' : 'border-box',
                'width'      : this.options.colwidth == 'auto' ? '' : this.options.colwidth
            };

            if (this.options.gutter) {
                css['padding-left']   = this.options.gutter;
                css['padding-bottom'] = this.options.gutter;

                this.element.css('margin-left', this.options.gutter * -1);
            }

            children.attr('data-grid-prepared', 'true').css(css);
        },

        updateLayout: function(elements) {

            this._prepareElements();

            elements = elements || this.element.children(':visible');

            var $this     = this,
                gutter    = this.options.gutter,
                children  = elements,
                maxwidth  = this.element.width() + (2*gutter) + 2,
                left      = 0,
                top       = 0,
                positions = [],

                item, width, height, pos, aX, aY, i, z, max, size;

            this.trigger('beforeupdate.uk.grid', [children]);

            children.each(function(index){

                size   = getElementSize(this);

                item   = UI.$(this);
                width  = size.outerWidth;
                height = size.outerHeight;
                left   = 0;
                top    = 0;

                for (i=0,max=positions.length;i<max;i++) {

                    pos = positions[i];

                    if (left <= pos.aX) { left = pos.aX; }
                    if (maxwidth < (left + width)) { left = 0; }
                    if (top <= pos.aY) { top = pos.aY; }
                }

                positions.push({
                    "ele"    : item,
                    "top"    : top,
                    "left"   : left,
                    "width"  : width,
                    "height" : height,
                    "aY"     : (top  + height),
                    "aX"     : (left + width)
                });
            });

            var posPrev, maxHeight = 0;

            // fix top
            for (i=0,max=positions.length;i<max;i++) {

                pos = positions[i];
                top = 0;

                for (z=0;z<i;z++) {

                    posPrev = positions[z];

                    // (posPrev.left + 1) fixex 1px bug when using % based widths
                    if (pos.left < posPrev.aX && (posPrev.left +1) < pos.aX) {
                        top = posPrev.aY;
                    }
                }

                pos.top = top;
                pos.aY  = top + pos.height;

                maxHeight = Math.max(maxHeight, pos.aY);
            }

            maxHeight = maxHeight - gutter;

            if (this.options.animation) {

                this.element.stop().animate({'height': maxHeight}, 100);

                positions.forEach(function(pos){
                    pos.ele.stop().animate({"top": pos.top, "left": pos.left, opacity: 1}, this.options.duration);
                }.bind(this));

            } else {

                this.element.css('height', maxHeight);

                positions.forEach(function(pos){
                    pos.ele.css({"top": pos.top, "left": pos.left, opacity: 1});
                }.bind(this));
            }

            // make sure to trigger possible scrollpies etc.
            setTimeout(function() {
                UI.$doc.trigger('scrolling.uk.document');
            }, 2 * this.options.duration * (this.options.animation ? 1:0));

            this.trigger('afterupdate.uk.grid', [children]);
        },

        filter: function(filter) {

            filter = filter || [];

            if (typeof(filter) === 'string') {
                filter = filter.split(/,/).map(function(item){ return item.trim(); });
            }

            var $this = this, children = this.element.children(), elements = {"visible": [], "hidden": []}, visible, hidden;

            children.each(function(index){

                var ele = UI.$(this), f = ele.attr('data-uk-filter'), infilter = filter.length ? false : true;

                if (f) {

                    f = f.split(/,/).map(function(item){ return item.trim(); });

                    filter.forEach(function(item){
                        if (f.indexOf(item) > -1) infilter = true;
                    });
                }

                elements[infilter ? "visible":"hidden"].push(ele);
            });

            // convert to jQuery collections
            elements.hidden  = UI.$(elements.hidden).map(function () {return this[0];});
            elements.visible = UI.$(elements.visible).map(function () {return this[0];});

            elements.hidden.attr('aria-hidden', 'true').filter(':visible').fadeOut(this.options.duration);
            elements.visible.attr('aria-hidden', 'false').filter(':hidden').css('opacity', 0).show();

            $this.updateLayout(elements.visible);
        },

        sort: function(by, order){

            order = order || 1;

            // covert from string (asc|desc) to number
            if (typeof(order) === 'string') {
                order = order.toLowerCase() == 'desc' ? -1 : 1;
            }

            var elements = this.element.children();

            elements.sort(function(a, b){

                a = UI.$(a);
                b = UI.$(b);

                return (b.data(by) || '') < (a.data(by) || '') ? order : (order*-1);

            }).appendTo(this.element);

            this.updateLayout(elements.filter(':visible'));
        }
    });

    /*!
    * getSize v1.2.2
    * measure size of elements
    * MIT license
    * https://github.com/desandro/get-size
    */
    var _getSize = (function(){

        var prefixes = 'Webkit Moz ms Ms O'.split(' ');
        var docElemStyle = document.documentElement.style;

        function getStyleProperty( propName ) {
            if ( !propName ) {
                return;
            }

            // test standard property first
            if ( typeof docElemStyle[ propName ] === 'string' ) {
                return propName;
            }

            // capitalize
            propName = propName.charAt(0).toUpperCase() + propName.slice(1);

            // test vendor specific properties
            var prefixed;
            for ( var i=0, len = prefixes.length; i < len; i++ ) {
                prefixed = prefixes[i] + propName;
                if ( typeof docElemStyle[ prefixed ] === 'string' ) {
                    return prefixed;
                }
            }
        }

        // -------------------------- helpers -------------------------- //

        // get a number from a string, not a percentage
        function getStyleSize( value ) {
            var num = parseFloat( value );
            // not a percent like '100%', and a number
            var isValid = value.indexOf('%') === -1 && !isNaN( num );
            return isValid && num;
        }

        function noop() {}

        var logError = typeof console === 'undefined' ? noop : function( message ) {
            console.error( message );
        };

        // -------------------------- measurements -------------------------- //

        var measurements = [
            'paddingLeft',
            'paddingRight',
            'paddingTop',
            'paddingBottom',
            'marginLeft',
            'marginRight',
            'marginTop',
            'marginBottom',
            'borderLeftWidth',
            'borderRightWidth',
            'borderTopWidth',
            'borderBottomWidth'
        ];

        function getZeroSize() {
            var size = {
                width: 0,
                height: 0,
                innerWidth: 0,
                innerHeight: 0,
                outerWidth: 0,
                outerHeight: 0
            };
            for ( var i=0, len = measurements.length; i < len; i++ ) {
                var measurement = measurements[i];
                size[ measurement ] = 0;
            }
            return size;
        }

        // -------------------------- setup -------------------------- //

        var isSetup = false;
        var getStyle, boxSizingProp, isBoxSizeOuter;

        /**
        * setup vars and functions
        * do it on initial getSize(), rather than on script load
        * For Firefox bug https://bugzilla.mozilla.org/show_bug.cgi?id=548397
        */
        function setup() {
            // setup once
            if ( isSetup ) {
                return;
            }
            isSetup = true;

            var getComputedStyle = window.getComputedStyle;
            getStyle = ( function() {
                var getStyleFn = getComputedStyle ?
                function( elem ) {
                    return getComputedStyle( elem, null );
                } :
                function( elem ) {
                    return elem.currentStyle;
                };

                return function getStyle( elem ) {
                    var style = getStyleFn( elem );
                    if ( !style ) {
                        logError( 'Style returned ' + style +
                        '. Are you running this code in a hidden iframe on Firefox? ' +
                        'See http://bit.ly/getsizebug1' );
                    }
                    return style;
                };
            })();

            // -------------------------- box sizing -------------------------- //

            boxSizingProp = getStyleProperty('boxSizing');

            /**
            * WebKit measures the outer-width on style.width on border-box elems
            * IE & Firefox measures the inner-width
            */
            if ( boxSizingProp ) {
                var div = document.createElement('div');
                div.style.width = '200px';
                div.style.padding = '1px 2px 3px 4px';
                div.style.borderStyle = 'solid';
                div.style.borderWidth = '1px 2px 3px 4px';
                div.style[ boxSizingProp ] = 'border-box';

                var body = document.body || document.documentElement;
                body.appendChild( div );
                var style = getStyle( div );

                isBoxSizeOuter = getStyleSize( style.width ) === 200;
                body.removeChild( div );
            }

        }

        // -------------------------- getSize -------------------------- //

        function getSize( elem ) {
            setup();

            // use querySeletor if elem is string
            if ( typeof elem === 'string' ) {
                elem = document.querySelector( elem );
            }

            // do not proceed on non-objects
            if ( !elem || typeof elem !== 'object' || !elem.nodeType ) {
                return;
            }

            var style = getStyle( elem );

            // if hidden, everything is 0
            if ( style.display === 'none' ) {
                return getZeroSize();
            }

            var size = {};
            size.width = elem.offsetWidth;
            size.height = elem.offsetHeight;

            var isBorderBox = size.isBorderBox = !!( boxSizingProp &&
                style[ boxSizingProp ] && style[ boxSizingProp ] === 'border-box' );

            // get all measurements
            for ( var i=0, len = measurements.length; i < len; i++ ) {
                var measurement = measurements[i];
                var value = style[ measurement ];

                var num = parseFloat( value );
                // any 'auto', 'medium' value will be 0
                size[ measurement ] = !isNaN( num ) ? num : 0;
            }

            var paddingWidth = size.paddingLeft + size.paddingRight;
            var paddingHeight = size.paddingTop + size.paddingBottom;
            var marginWidth = size.marginLeft + size.marginRight;
            var marginHeight = size.marginTop + size.marginBottom;
            var borderWidth = size.borderLeftWidth + size.borderRightWidth;
            var borderHeight = size.borderTopWidth + size.borderBottomWidth;

            var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter;

            // overwrite width and height if we can get it from style
            var styleWidth = getStyleSize( style.width );
            if ( styleWidth !== false ) {
                size.width = styleWidth +
                // add padding and border unless it's already including it
                ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth );
            }

            var styleHeight = getStyleSize( style.height );
            if ( styleHeight !== false ) {
                size.height = styleHeight +
                // add padding and border unless it's already including it
                ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight );
            }

            size.innerWidth = size.width - ( paddingWidth + borderWidth );
            size.innerHeight = size.height - ( paddingHeight + borderHeight );

            size.outerWidth = size.width + marginWidth;
            size.outerHeight = size.height + marginHeight;

            return size;
        }

        return getSize;

    })();

    function getElementSize(ele) {
        return _getSize(ele);
    }
});
<|endoftext|>


<commit_before>define([], () => {
  return {
    defaultLanguage: 'en',
    supports: ['en', 'ko', 'fr', 'ch', 'de', 'nl', 'jp', 'pt', 'da', 'pl', 'es', 'cs', 'uk', 'ru'],
    weeks: {
      en: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
      ko: ['일', '월', '화', '수', '목', '금', '토'],
      fa: ['شنبه', 'آدینه', 'پنج', 'چهار', 'سه', 'دو', 'یک'],
      fr: ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'],
      ch: ['日', '一', '二', '三', '四', '五', '六'],
      de: ['SO', 'MO', 'DI', 'MI', 'DO', 'FR', 'SA'],
      nl: ['Zo', 'Ma', 'Di', 'Wo', 'Do', 'Vr', 'Za'],
      jp: ['日', '月', '火', '水', '木', '金', '土'],
      pt: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sab'],
      da: ['Søn', 'Man', 'Tir', 'Ons', 'Tor', 'Fre', 'Lør'],
      pl: ['Nie', 'Pon', 'Wto', 'Śro', 'Czw', 'Pią', 'Sob'],
      es: ['Dom', 'Lun', 'Mar', 'Mié', 'Jue', 'Vie', 'Sáb'],
      it: ['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab'],
      cs: ['Ne', 'Po', 'Út', 'St', 'Čt', 'Pá', 'So'],
      uk: ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Нд'],
      ru: ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс'],
    },
    monthsLong: {
      en: ['January', 'February', 'March', 'April', 'May', 'Jun', 'July', 'August', 'September', 'October', 'November', 'December'],
      ko: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'],
      fa: ['آذر', 'آبان', 'مهر', 'شهریور', 'مرداد', 'تیر', 'خرداد', 'اردیبهشت', 'فروردین', 'اسفند', 'بهمن', 'دی'],
      fr: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
      ch: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
      de: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
      nl: ['Januari', 'Februari', 'Maart', 'April', 'Mei', 'Juni', 'Juli', 'Augustus', 'September', 'Oktober', 'November', 'December'],
      jp: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
      pt: ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'],
      da: ['Januar', 'Februar', 'Marts', 'April', 'Maj', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'December'],
      pl: ['Styczeń', 'Luty', 'Marzec', 'Kwiecień', 'Maj', 'Czerwiec', 'Lipiec', 'Sierpień', 'Wrzesień', 'Październik', 'Listopad', 'Grudzień'],
      es: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
      it: ['Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre'],
      cs: ['Leden', 'Únor', 'Březen', 'Duben', 'Květen', 'Červen', 'Cervenec', 'Srpen', 'Září', 'Říjen', 'Listopad', 'Prosinec'],
      uk: ['Січень', 'Лютий', 'Березень', 'Квітень', 'Травень', 'Червень', 'Липень', 'Серпень', 'Вересень', 'Жовтень', 'Листопад', 'Грудень'],
      ru: ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь']
    },
    months: {
      en: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
      ko: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'],
      fa: ['آذر', 'آبان', 'مهر', 'شهریور', 'مرداد', 'تیر', 'خرداد', 'اردیبهشت', 'فروردین', 'اسفند', 'بهمن', 'دی'],
      fr: ['Jan', 'Fév', 'Mar', 'Avr', 'Mai', 'Juin', 'Juil', 'Aoû', 'Sep', 'Oct', 'Nov', 'Déc'],
      ch: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
      de: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
      nl: ['Jan', 'Feb', 'Mrt', 'Apr', 'Mei', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'],
      jp: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
      pt: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez'],
      da: ['Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'],
      pl: ['Sty', 'Lut', 'Mar', 'Kwi', 'Maj', 'Cze', 'Lip', 'Sie', 'Wrz', 'Paź', 'Lis', 'Gru'],
      es: ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dec'],
      it: ['Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago', 'Set', 'Ott', 'Nov', 'Dic'],
      cs: ['Led', 'Úno', 'Bře', 'Dub', 'Kvě', 'Čvn', 'Čvc', 'Srp', 'Zář', 'Říj', 'Lis', 'Pro'],
      uk: ['Січ', 'Лют', 'Бер', 'Квіт', 'Трав', 'Черв', 'Лип', 'Серп', 'Вер', 'Жовт', 'Лист', 'Груд'],
      ru: ['Янв', 'Февр', 'Март', 'Апр', 'Май', 'Июнь', 'Июль', 'Авг', 'Сент', 'Окт', 'Нояб', 'Дек'],
    },
    controls: {
      en: {
        ok: 'OK',
        cancel: 'Cancel'
      },
      ko: {
        ok: '확인',
        cancel: '취소'
      },
      fa: {
        ok: 'چک کنید',
        cancel: 'لغو'
      },
      fr: {
        ok: 'Vérifier',
        cancel: 'Annuler'
      },
      ch: {
        ok: '确认',
        cancel: '取消'
      },
      de: {
        ok: 'Okay',
        cancel: 'Abbrechen'
      },
      nl: {
        ok: 'Oké',
        cancel: 'Annuleren'
      },
      jp: {
        ok: '確認',
        cancel: 'キャンセル'
      },
      pt: {
        ok: 'Verifique',
        cancel: 'Cancelar'
      },
      da: {
        ok: 'Bekræftelse',
        cancel: 'aflyst'
      },
      pl: {
        ok: 'Sprawdź',
        cancel: 'Anuluj'
      },
      es: {
        ok: 'Cheque',
        cancel: 'Cancelar'
      },
      it: {
        ok: 'conferma',
        cancel: 'annullato'
      },
      cs: {
        ok: 'Zkontrolujte',
        cancel: 'Zrušit'
      },
      uk: {
        ok: 'Вибрати',
        cancel: 'Скасувати'
      },
      ru: {
        ok: 'Выбрать',
        cancel: 'Отмена'
      }
    }
  };
});
<commit_msg>fix weeks starting with sunday (#81)<commit_after>define([], () => {
  return {
    defaultLanguage: 'en',
    supports: ['en', 'ko', 'fr', 'ch', 'de', 'nl', 'jp', 'pt', 'da', 'pl', 'es', 'cs', 'uk', 'ru'],
    weeks: {
      en: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
      ko: ['일', '월', '화', '수', '목', '금', '토'],
      fa: ['شنبه', 'آدینه', 'پنج', 'چهار', 'سه', 'دو', 'یک'],
      fr: ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'],
      ch: ['日', '一', '二', '三', '四', '五', '六'],
      de: ['SO', 'MO', 'DI', 'MI', 'DO', 'FR', 'SA'],
      nl: ['Zo', 'Ma', 'Di', 'Wo', 'Do', 'Vr', 'Za'],
      jp: ['日', '月', '火', '水', '木', '金', '土'],
      pt: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sab'],
      da: ['Søn', 'Man', 'Tir', 'Ons', 'Tor', 'Fre', 'Lør'],
      pl: ['Nie', 'Pon', 'Wto', 'Śro', 'Czw', 'Pią', 'Sob'],
      es: ['Dom', 'Lun', 'Mar', 'Mié', 'Jue', 'Vie', 'Sáb'],
      it: ['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab'],
      cs: ['Ne', 'Po', 'Út', 'St', 'Čt', 'Pá', 'So'],
      uk: ['Нд', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'],
      ru: ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'],
    },
    monthsLong: {
      en: ['January', 'February', 'March', 'April', 'May', 'Jun', 'July', 'August', 'September', 'October', 'November', 'December'],
      ko: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'],
      fa: ['آذر', 'آبان', 'مهر', 'شهریور', 'مرداد', 'تیر', 'خرداد', 'اردیبهشت', 'فروردین', 'اسفند', 'بهمن', 'دی'],
      fr: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
      ch: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
      de: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
      nl: ['Januari', 'Februari', 'Maart', 'April', 'Mei', 'Juni', 'Juli', 'Augustus', 'September', 'Oktober', 'November', 'December'],
      jp: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
      pt: ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'],
      da: ['Januar', 'Februar', 'Marts', 'April', 'Maj', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'December'],
      pl: ['Styczeń', 'Luty', 'Marzec', 'Kwiecień', 'Maj', 'Czerwiec', 'Lipiec', 'Sierpień', 'Wrzesień', 'Październik', 'Listopad', 'Grudzień'],
      es: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
      it: ['Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre'],
      cs: ['Leden', 'Únor', 'Březen', 'Duben', 'Květen', 'Červen', 'Cervenec', 'Srpen', 'Září', 'Říjen', 'Listopad', 'Prosinec'],
      uk: ['Січень', 'Лютий', 'Березень', 'Квітень', 'Травень', 'Червень', 'Липень', 'Серпень', 'Вересень', 'Жовтень', 'Листопад', 'Грудень'],
      ru: ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь']
    },
    months: {
      en: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
      ko: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'],
      fa: ['آذر', 'آبان', 'مهر', 'شهریور', 'مرداد', 'تیر', 'خرداد', 'اردیبهشت', 'فروردین', 'اسفند', 'بهمن', 'دی'],
      fr: ['Jan', 'Fév', 'Mar', 'Avr', 'Mai', 'Juin', 'Juil', 'Aoû', 'Sep', 'Oct', 'Nov', 'Déc'],
      ch: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
      de: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
      nl: ['Jan', 'Feb', 'Mrt', 'Apr', 'Mei', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'],
      jp: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
      pt: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez'],
      da: ['Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'],
      pl: ['Sty', 'Lut', 'Mar', 'Kwi', 'Maj', 'Cze', 'Lip', 'Sie', 'Wrz', 'Paź', 'Lis', 'Gru'],
      es: ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dec'],
      it: ['Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago', 'Set', 'Ott', 'Nov', 'Dic'],
      cs: ['Led', 'Úno', 'Bře', 'Dub', 'Kvě', 'Čvn', 'Čvc', 'Srp', 'Zář', 'Říj', 'Lis', 'Pro'],
      uk: ['Січ', 'Лют', 'Бер', 'Квіт', 'Трав', 'Черв', 'Лип', 'Серп', 'Вер', 'Жовт', 'Лист', 'Груд'],
      ru: ['Янв', 'Февр', 'Март', 'Апр', 'Май', 'Июнь', 'Июль', 'Авг', 'Сент', 'Окт', 'Нояб', 'Дек'],
    },
    controls: {
      en: {
        ok: 'OK',
        cancel: 'Cancel'
      },
      ko: {
        ok: '확인',
        cancel: '취소'
      },
      fa: {
        ok: 'چک کنید',
        cancel: 'لغو'
      },
      fr: {
        ok: 'Vérifier',
        cancel: 'Annuler'
      },
      ch: {
        ok: '确认',
        cancel: '取消'
      },
      de: {
        ok: 'Okay',
        cancel: 'Abbrechen'
      },
      nl: {
        ok: 'Oké',
        cancel: 'Annuleren'
      },
      jp: {
        ok: '確認',
        cancel: 'キャンセル'
      },
      pt: {
        ok: 'Verifique',
        cancel: 'Cancelar'
      },
      da: {
        ok: 'Bekræftelse',
        cancel: 'aflyst'
      },
      pl: {
        ok: 'Sprawdź',
        cancel: 'Anuluj'
      },
      es: {
        ok: 'Cheque',
        cancel: 'Cancelar'
      },
      it: {
        ok: 'conferma',
        cancel: 'annullato'
      },
      cs: {
        ok: 'Zkontrolujte',
        cancel: 'Zrušit'
      },
      uk: {
        ok: 'Вибрати',
        cancel: 'Скасувати'
      },
      ru: {
        ok: 'Выбрать',
        cancel: 'Отмена'
      }
    }
  };
});
<|endoftext|>


<commit_before>define(function(require) {
    'use strict';

    var appController = require('js/app-controller'),
        download = require('js/util/download'),
        angular = require('angular'),
        emailDao, crypto, keychain;

    //
    // Controller
    //

    var ReadCtrl = function($scope) {

        emailDao = appController._emailDao;
        crypto = appController._crypto;
        keychain = appController._keychain;

        // set default value so that the popover height is correct on init
        $scope.keyId = 'XXXXXXXX';

        $scope.state.read = {
            open: false,
            toggle: function(to) {
                this.open = to;
            }
        };

        $scope.lineEmpty = function(line) {
            return line.replace(/>/g, '').trim().length === 0;
        };

        $scope.getKeyId = function(address) {
            $scope.keyId = 'unknown user';
            keychain.getReceiverPublicKey(address, function(err, pubkey) {
                if (err) {
                    $scope.onError(err);
                    return;
                }

                if (!pubkey) {
                    return;
                }

                var fpr = crypto.getFingerprint(pubkey.publicKey);
                var formatted = fpr.slice(32);

                $scope.keyId = formatted;
                $scope.$apply();
            });
        };

        $scope.$watch('state.mailList.selected', function() {
            var mail = $scope.state.mailList.selected;
            if (!mail) {
                return;
            }

            // display sender security status
            mail.from.forEach(checkPublicKey);
            // display recipient security status
            mail.to.forEach(checkPublicKey);
            // display recipient security status
            Array.isArray(mail.cc) && mail.cc.forEach(checkPublicKey);
        });

        function checkPublicKey(user) {
            user.secure = undefined;
            keychain.getReceiverPublicKey(user.address, function(err, pubkey) {
                if (err) {
                    $scope.onError(err);
                    return;
                }

                if (pubkey && pubkey.publicKey) {
                    user.secure = true;
                } else {
                    user.secure = false;
                }

                $scope.$apply();
            });
        }

        $scope.download = function(attachment) {
            // download file to disk if content is available
            if (attachment.content) {
                saveToDisk(attachment);
                return;
            }

            var folder = $scope.state.nav.currentFolder;
            var email = $scope.state.mailList.selected;

            emailDao.getAttachment({
                path: folder.path,
                uid: email.uid,
                attachment: attachment
            }, function(err) {
                if (err) {
                    $scope.onError(err);
                    return;
                }

                saveToDisk(attachment);
            });

            function saveToDisk(attachment) {
                download.createDownload({
                    content: attachment.content,
                    filename: attachment.filename,
                    contentType: attachment.mimeType
                }, $scope.onError);
            }
        };
    };

    //
    // Directives
    //

    var ngModule = angular.module('read', []);
    ngModule.directive('frameLoad', function() {
        return function(scope, elm) {
            elm.bind('load', function() {
                var frame = elm[0];
                frame.height = frame.contentWindow.document.body.scrollHeight + 'px';
            });
        };
    });

    return ReadCtrl;
});<commit_msg>fix watch of selected mail in reader<commit_after>define(function(require) {
    'use strict';

    var appController = require('js/app-controller'),
        download = require('js/util/download'),
        angular = require('angular'),
        emailDao, crypto, keychain;

    //
    // Controller
    //

    var ReadCtrl = function($scope) {

        emailDao = appController._emailDao;
        crypto = appController._crypto;
        keychain = appController._keychain;

        // set default value so that the popover height is correct on init
        $scope.keyId = 'XXXXXXXX';

        $scope.state.read = {
            open: false,
            toggle: function(to) {
                this.open = to;
            }
        };

        $scope.lineEmpty = function(line) {
            return line.replace(/>/g, '').trim().length === 0;
        };

        $scope.getKeyId = function(address) {
            $scope.keyId = 'unknown user';
            keychain.getReceiverPublicKey(address, function(err, pubkey) {
                if (err) {
                    $scope.onError(err);
                    return;
                }

                if (!pubkey) {
                    return;
                }

                var fpr = crypto.getFingerprint(pubkey.publicKey);
                var formatted = fpr.slice(32);

                $scope.keyId = formatted;
                $scope.$apply();
            });
        };

        $scope.$watch('state.mailList.selected', function(mail) {
            if (!mail) {
                return;
            }

            // display sender security status
            mail.from.forEach(checkPublicKey);
            // display recipient security status
            mail.to.forEach(checkPublicKey);
            // display recipient security status
            Array.isArray(mail.cc) && mail.cc.forEach(checkPublicKey);
        });

        function checkPublicKey(user) {
            user.secure = undefined;
            keychain.getReceiverPublicKey(user.address, function(err, pubkey) {
                if (err) {
                    $scope.onError(err);
                    return;
                }

                if (pubkey && pubkey.publicKey) {
                    user.secure = true;
                } else {
                    user.secure = false;
                }

                $scope.$apply();
            });
        }

        $scope.download = function(attachment) {
            // download file to disk if content is available
            if (attachment.content) {
                saveToDisk(attachment);
                return;
            }

            var folder = $scope.state.nav.currentFolder;
            var email = $scope.state.mailList.selected;

            emailDao.getAttachment({
                path: folder.path,
                uid: email.uid,
                attachment: attachment
            }, function(err) {
                if (err) {
                    $scope.onError(err);
                    return;
                }

                saveToDisk(attachment);
            });

            function saveToDisk(attachment) {
                download.createDownload({
                    content: attachment.content,
                    filename: attachment.filename,
                    contentType: attachment.mimeType
                }, $scope.onError);
            }
        };
    };

    //
    // Directives
    //

    var ngModule = angular.module('read', []);
    ngModule.directive('frameLoad', function() {
        return function(scope, elm) {
            elm.bind('load', function() {
                var frame = elm[0];
                frame.height = frame.contentWindow.document.body.scrollHeight + 'px';
            });
        };
    });

    return ReadCtrl;
});<|endoftext|>