<commit_before>/**********************************************************************************\
** The MIT License (MIT)                                                          **
**                                                                                **
** Copyright (c) 2014 Chris 'CJ' Jones                                            **
**                                                                                **
** Permission is hereby granted, free of charge, to any person obtaining a copy   **
** of this software and associated documentation files (the "Software"), to deal  **
** in the Software without restriction, including without limitation the rights   **
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell      **
** copies of the Software, and to permit persons to whom the Software is          **
** furnished to do so, subject to the following conditions:                       **
**                                                                                **
** The above copyright notice and this permission notice shall be included in all **
** copies or substantial portions of the Software.                                **
**                                                                                **
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR     **
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,       **
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE    **
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER         **
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,  **
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE  **
** SOFTWARE.                                                                      **
\**********************************************************************************/

(function($) {
    /* Start Objct Constructor */
    var LimitTextarea = function(element, options) {
        /* Start Private Variables */
        var
            e = element,
            id = $(e).attr('id'),
            idPart = '-limitTextarea-',
            keyState = {
                shift: {
                    keyCode: 16,
                    down: false
                },
                ctrl: {
                    keyCode: 17,
                    down: false
                },
                alt: {
                    keyCode: 18,
                    down: false
                },
                altgr: {
                    keyCode: 225,
                    down: false
                }
            },
            overridable = [
                'afterInit',
                'afterKeyDown',
                'afterKeyUp',
                'afterMouseDown',
                'afterMouseUp',
                'afterContextMenu',
                'afterCut',
                'afterPaste',
                'afterEmpty',
                'afterNotEmpty',
                'afterWithinLimit',
                'afterReachLimit',
            ]
        ;
        /* End Private Variables */

        /* Start Private Functions */
        function setKeyState(keyCode, state) {
            var state = (state) ? true : false;
            switch(keyCode) {
                case 16:
                    keyState.shift.down = state;
                    break;
                case 17:
                    keyState.ctrl.down = state;
                    break;
                case 18:
                    keyState.alt.down = state;
                    break;
                case 225:
                    keyState.altgr.down = state;
                    break;
            }
        }
        /* End Private Functions */

        /* Start Create Object */
        var obj = {
            /* Start Main Functions */
            init: function() {
                var t = this;
                if(!t.initialized) {
                    $(e).data('limittextareaLimit', ($(e).data('limittextareaLimit')) ? $(e).data('limittextareaLimit') : (options && options.limit) ? options.limit : 0);
                    $(e).data('limittextareaCharactercount', ($(e).data('limittextareaCharactercount')) ? $(e).data('limittextareaCharactercount') : 0);
                    $(e).data('limittextareaIsempty', ($(e).data('limittextareaIsempty')) ? $(e).data('limittextareaIsempty') : false);
                    $(e).data('limittextareaLimitreached', ($(e).data('limittextareaLimitreached')) ? $(e).data('limittextareaLimitreached') : false);

                    t.characterCount();
                    t.drawCounter();
                    t.addStyles();
                    t.initialized = true;
                }
                t.afterInit();
                return t;
            },
            drawCounter: function() {
                var
                    t = this,
                    container = document.createElement('div'),
                    counterContainer = document.createElement('span')
                ;

                counterContainer.id = id + idPart + 'characterCount';

                container.id = id + idPart + 'container';
                container.innerHTML = counterContainer.outerHTML + ' of ' + $(e).data('limittextareaLimit') + ' characters remaining'
                $(e).after(container);
                t.updateCounter();

                return t;
            },
            addStyles: function() {
                var t = this;
                if(!$('#' + id + idPart + 'container').hasClass('limitTextarea')) $('#' + id + idPart + 'container').addClass('limitTextarea');
                if(!$('#' + id + idPart + 'container').hasClass('container')) $('#' + id + idPart + 'container').addClass('container');
                if(!$('#' + id + idPart + 'characterCount').hasClass('limitTextarea')) $('#' + id + idPart + 'characterCount').addClass('limitTextarea');
                if(!$('#' + id + idPart + 'characterCount').hasClass('counterContainer')) $('#' + id + idPart + 'characterCount').addClass('counterContainer');
                return t;
            },
            updateCounter: function() {
                var
                    t = this,
                    remaining = parseInt($(e).data('limittextareaLimit')) - parseInt($(e).data('limittextareaCharactercount'))
                ;
                $('#' + id + idPart + 'characterCount').html(remaining);
                return t;
            },
            /* End Main Functions */

            /* Start Events */
            /* Start Non Editable Events */
            onKeyDown: function(event) {
                var
                    t = this,
                    keyCode = event.keyCode,
                    key = String.fromCharCode(keyCode),
                    allowedKeys = [
                        8,
                        9,
                        16,
                        17,
                        19,
                        20,
                        27,
                        35,
                        36,
                        37,
                        38,
                        39,
                        40,
                        45,
                        46,
                        144,
                        145
                    ],
                    allowedCtrlKeys = [
                        35,
                        36,
                        67,
                        86,
                        88,
                        89,
                        90,
                    ],
                    allowedShiftKeys = [
                        35,
                        36,
                        37,
                        38,
                        39,
                        40
                    ],
                    testKeys = $.inArray(parseInt(keyCode), allowedKeys),
                    testCtrlKeys = $.inArray(parseInt(keyCode), allowedCtrlKeys),
                    testShiftKeys = $.inArray(parseInt(keyCode), allowedShiftKeys)
                ;
                if($(e).data('limittextareaLimitreached')) {
                    if(
                       !(keyState.ctrl.down && testCtrlKeys >= 0) &&
                       !(keyState.shift.down && testShiftKeys >= 0) &&
                       (testKeys < 0)
                    ) {
                        event.preventDefault();
                    }
                }
                window.setTimeout(
                    function() {
                        $(e).trigger('afterKeyDown');
                    },
                    0
                );
                return t;
            },
            onKeyUp: function(event) {
                var
                    t = this,
                    keyCode = event.keyCode,
                    key = String.fromCharCode(keyCode)
                ;
                window.setTimeout(
                    function() {
                        $(e).trigger('afterKeyUp');
                    },
                    0
                );
                return t;
            },
            onMouseDown: function(event) {
                var t = this;
                window.setTimeout(
                    function() {
                        $(e).trigger('afterMouseDown');
                    },
                    0
                );
                return t;
            },
            onMouseUp: function(event) {
                var t = this;
                window.setTimeout(
                    function() {
                        $(e).trigger('afterMouseUp');
                    },
                    0
                );
                return t;
            },
            onContextMenu: function(event) {
                var t = this;
                window.setTimeout(
                    function() {
                        $(e).trigger('afterContextMenu');
                    },
                    0
                );
                return t;
            },
            onCut: function(event) {
                var t = this;
                window.setTimeout(
                    function() {
                        $(e).trigger('afterCut');
                    },
                    0
                );
                return t;
            },
            onPaste: function(event) {
                var t = this;
                window.setTimeout(
                    function() {
                        $(e).trigger('afterPaste');
                    },
                    0
                );
                return t;
            },
            onEmpty: function() {
                var t = this;
                $(e).data('limittextareaIsempty', true);
                window.setTimeout(
                    function() {
                        $(e).trigger('afterEmpty');
                    },
                    0
                );
                return t;
            },
            onNotEmpty: function() {
                var t = this;
                $(e).data('limittextareaIsempty', false);
                window.setTimeout(
                    function() {
                        $(e).trigger('afterNotEmpty');
                    },
                    0
                );
                return t;
            },
            onWithinLimit: function(event) {
                var t = this;
                $(e).data('limittextareaLimitreached', false);
                window.setTimeout(
                    function() {
                        $(e).trigger('afterWithinLimit');
                    },
                    0
                );
                return t;
            },
            onReachLimit: function(event) {
                var t = this;
                $(e).data('limittextareaLimitreached', true);
                $(e).val($(e).val().substr(0, $(e).data('limittextareaLimit')));
                if($(e).data('limittextareaCharactercount') > $(e).data('limittextareaLimit'))
                    $(e).data('limittextareaCharactercount', $(e).data('limittextareaLimit'));
                window.setTimeout(
                    function() {
                        $(e).trigger('afterReachLimit');
                    },
                    0
                );
                return t;
            },
            /* End Non Editable Events */
            /* Start Editable Events */
            afterInit: function() {
                var t = this;
                return t;
            },
            afterKeyDown: function(event) {
                var t = this;
                return t;
            },
            afterKeyUp: function(event) {
                var t = this;
                return t;
            },
            afterMouseDown: function(event) {
                var t = this;
                return t;
            },
            afterMouseUp: function(event) {
                var t = this;
                return t;
            },
            afterContextMenu: function(event) {
                var t = this;
                return t;
            },
            afterCut: function(event) {
                var t = this;
                return t;
            },
            afterPaste: function(event) {
                var t = this;
                return t;
            },
            afterEmpty: function(event) {
                var t = this;
                return t;
            },
            afterNotEmpty: function(event) {
                var t = this;
                return t;
            },
            afterWithinLimit: function(event) {
                var t = this;
                return t;
            },
            afterReachLimit: function(event) {
                var t = this;
                return t;
            },
            /* End Editable Events */
            /* End Events */

            /* Start Properties */
            characterCount: function(val) {
                var
                    t = this,
                    returns = t,
                    remaining = 0
                ;

                if(val) {
                    $(e).data('limittextareaCharactercount', val);
                } else {
                    $(e).data('limittextareaCharactercount', $(e).val().length);
                    returns = $(e).data('limittextareaCharactercount');

                    if($(e).val().length == 0) {
                        $(e).trigger('empty');
                    } else {
                        $(e).trigger('notempty');
                    }

                    if($(e).data('limittextareaLimit')) {
                        if(parseInt($(e).data('limittextareaCharactercount')) + 1 > parseInt($(e).data('limittextareaLimit'))) {
                            $(e).trigger('reachlimit');
                        } else {
                            $(e).trigger('withinlimit');
                        }
                    } else {
                        //Error
                    }
                }

                if(t.initialized)
                    t.updateCounter();

                return returns;
            },
            limit: function(val) {
                return (val) ? $(e).data('limittextareaLimit', val) : $(e).data('limittextareaLimit');
            }
            /* End Properties */
        };
        /* End Create Object */

        /* Start Set Options */
        if(options) {
            Object.keys(options).each(
                function(key) {
                    var value = options[key];
                    if($.inArray(key, overridable) >= 0) {
                        obj[key] = options[key];
                    }
                }
            );
        }
        /* End Set Options */

        /* Start Set Event Listeners */
        $(e).on('keydown', function(event) { setKeyState(event.keyCode, true); obj.onKeyDown(event).characterCount(); });
        $(e).on('keyup', function(event) { setKeyState(event.keyCode);obj.onKeyUp(event).characterCount(); });
        $(e).on('mouseDown', function(event) { obj.afterKeyUp(event).characterCount(); });
        $(e).on('mouseup', function(event) { obj.onMouseUp(event).characterCount(); });
        $(e).on('contextmenu', function(event) { obj.onContextMenu(event).characterCount(); });
        $(e).on('cut', function(event) { obj.onCut(event).characterCount(); });
        $(e).on('paste', function(event) { obj.onPaste(event).characterCount(); });

        $(e).on('empty', function(event) { obj.onEmpty(event); });
        $(e).on('notempty', function(event) { obj.onNotEmpty(event); });
        $(e).on('withinlimit', function(event) { obj.onWithinLimit(event); });
        $(e).on('reachlimit', function(event) { obj.onReachLimit(event); });

        $(e).on('afterKeyDown', function(event) { obj.afterKeyDown(event); });
        $(e).on('afterKeyUp', function(event) { obj.afterKeyUp(event); });
        $(e).on('afterMouseDown', function(event) { obj.afterKeyUp(event); });
        $(e).on('afterMouseUp', function(event) { obj.afterMouseUp(event); });
        $(e).on('afterContextMenu', function(event) { obj.afterContextMenu(event); });
        $(e).on('afterCut', function(event) { obj.afterCut(event); });
        $(e).on('afterPaste', function(event) { obj.afterPaste(event); });
        $(e).on('afterEmpty', function(event) { obj.afterEmpty(event); });
        $(e).on('afterNotEmpty', function(event) { obj.afterNotEmpty(event); });
        $(e).on('afterWithinLimit', function(event) { obj.afterWithinLimit(event); });
        $(e).on('afterReachLimit', function(event) { obj.afterReachLimit(event); });
        /* End Set Event Listeners */

        return obj;
    };
    /* End Objct Constructor */

    /* Start Add Method To jQuery */
    $.fn.extend({
        limitTextarea: function(options) {
            if(!$(this).data('limittextarea')) {
                var obj = new LimitTextarea(this, options);

                $(this).data('limittextarea', obj);

                obj.init();

                return this;
            } else {
                return $(this).data('limittextarea');
            }
        }
    });
    /* End Add Method To jQuery */
})(jQuery);
<commit_msg>Added disable context menu and settable properties<commit_after>/**********************************************************************************\
** The MIT License (MIT)                                                          **
**                                                                                **
** Copyright (c) 2014 Chris 'CJ' Jones                                            **
**                                                                                **
** Permission is hereby granted, free of charge, to any person obtaining a copy   **
** of this software and associated documentation files (the "Software"), to deal  **
** in the Software without restriction, including without limitation the rights   **
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell      **
** copies of the Software, and to permit persons to whom the Software is          **
** furnished to do so, subject to the following conditions:                       **
**                                                                                **
** The above copyright notice and this permission notice shall be included in all **
** copies or substantial portions of the Software.                                **
**                                                                                **
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR     **
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,       **
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE    **
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER         **
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,  **
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE  **
** SOFTWARE.                                                                      **
\**********************************************************************************/

(function($) {
    /* Start Objct Constructor */
    var LimitTextarea = function(element, options) {
        /* Start Private Variables */
        var
            e = element,
            id = $(e).attr('id'),
            idPart = '-limitTextarea-',
            keyState = {
                shift: {
                    keyCode: 16,
                    down: false
                },
                ctrl: {
                    keyCode: 17,
                    down: false
                },
                alt: {
                    keyCode: 18,
                    down: false
                },
                altgr: {
                    keyCode: 225,
                    down: false
                }
            },
            overridable = [
                'afterInit',
                'afterKeyDown',
                'afterKeyUp',
                'afterMouseDown',
                'afterMouseUp',
                'afterContextMenu',
                'afterCut',
                'afterPaste',
                'afterEmpty',
                'afterNotEmpty',
                'afterWithinLimit',
                'afterReachLimit'
            ],
            settable = [
                'limit',
                'disableContextMenu'
            ]
        ;
        /* End Private Variables */

        /* Start Private Functions */
        function setKeyState(keyCode, state) {
            var state = (state) ? true : false;
            switch(keyCode) {
                case 16:
                    keyState.shift.down = state;
                    break;
                case 17:
                    keyState.ctrl.down = state;
                    break;
                case 18:
                    keyState.alt.down = state;
                    break;
                case 225:
                    keyState.altgr.down = state;
                    break;
            }
        }
        /* End Private Functions */

        /* Start Create Object */
        var obj = {
            /* Start Main Functions */
            init: function() {
                var t = this;
                if(!t.initialized) {
                    $(e).data('limittextareaLimit', ($(e).data('limittextareaLimit')) ? $(e).data('limittextareaLimit') : (options && options.limit) ? options.limit : 0);
                    $(e).data('limittextareaCharactercount', ($(e).data('limittextareaCharactercount')) ? $(e).data('limittextareaCharactercount') : 0);
                    $(e).data('limittextareaIsempty', ($(e).data('limittextareaIsempty')) ? $(e).data('limittextareaIsempty') : false);
                    $(e).data('limittextareaLimitreached', ($(e).data('limittextareaLimitreached')) ? $(e).data('limittextareaLimitreached') : false);
                    $(e).data('limittextareaDisablecontextmenu', ($(e).data('limittextareaDisablecontextmenu')) ? $(e).data('limittextareaDisablecontextmenu') : 'false');

                    t.characterCount();
                    t.drawCounter();
                    t.addStyles();
                    t.initialized = true;
                }
                t.afterInit();
                return t;
            },
            drawCounter: function() {
                var
                    t = this,
                    container = document.createElement('div'),
                    counterContainer = document.createElement('span')
                ;

                counterContainer.id = id + idPart + 'characterCount';

                container.id = id + idPart + 'container';
                container.innerHTML = counterContainer.outerHTML + ' of ' + $(e).data('limittextareaLimit') + ' characters remaining'
                $(e).after(container);
                t.updateCounter();

                return t;
            },
            addStyles: function() {
                var t = this;
                if(!$('#' + id + idPart + 'container').hasClass('limitTextarea')) $('#' + id + idPart + 'container').addClass('limitTextarea');
                if(!$('#' + id + idPart + 'container').hasClass('container')) $('#' + id + idPart + 'container').addClass('container');
                if(!$('#' + id + idPart + 'characterCount').hasClass('limitTextarea')) $('#' + id + idPart + 'characterCount').addClass('limitTextarea');
                if(!$('#' + id + idPart + 'characterCount').hasClass('counterContainer')) $('#' + id + idPart + 'characterCount').addClass('counterContainer');
                return t;
            },
            updateCounter: function() {
                var
                    t = this,
                    remaining = parseInt($(e).data('limittextareaLimit')) - parseInt($(e).data('limittextareaCharactercount'))
                ;
                $('#' + id + idPart + 'characterCount').html(remaining);
                return t;
            },
            /* End Main Functions */

            /* Start Events */
            /* Start Non Editable Events */
            onKeyDown: function(event) {
                var
                    t = this,
                    keyCode = event.keyCode,
                    key = String.fromCharCode(keyCode),
                    allowedKeys = [
                        8,
                        9,
                        16,
                        17,
                        19,
                        20,
                        27,
                        35,
                        36,
                        37,
                        38,
                        39,
                        40,
                        45,
                        46,
                        144,
                        145
                    ],
                    allowedCtrlKeys = [
                        35,
                        36,
                        67,
                        86,
                        88,
                        89,
                        90,
                    ],
                    allowedShiftKeys = [
                        35,
                        36,
                        37,
                        38,
                        39,
                        40
                    ],
                    testKeys = $.inArray(parseInt(keyCode), allowedKeys),
                    testCtrlKeys = $.inArray(parseInt(keyCode), allowedCtrlKeys),
                    testShiftKeys = $.inArray(parseInt(keyCode), allowedShiftKeys)
                ;
                if($(e).data('limittextareaLimitreached')) {
                    if(
                       !(keyState.ctrl.down && testCtrlKeys >= 0) &&
                       !(keyState.shift.down && testShiftKeys >= 0) &&
                       (testKeys < 0)
                    ) {
                        event.preventDefault();
                    }
                }
                window.setTimeout(
                    function() {
                        $(e).trigger('afterKeyDown');
                    },
                    0
                );
                return t;
            },
            onKeyUp: function(event) {
                var
                    t = this,
                    keyCode = event.keyCode,
                    key = String.fromCharCode(keyCode)
                ;
                window.setTimeout(
                    function() {
                        $(e).trigger('afterKeyUp');
                    },
                    0
                );
                return t;
            },
            onMouseDown: function(event) {
                var t = this;
                window.setTimeout(
                    function() {
                        $(e).trigger('afterMouseDown');
                    },
                    0
                );
                return t;
            },
            onMouseUp: function(event) {
                var t = this;
                window.setTimeout(
                    function() {
                        $(e).trigger('afterMouseUp');
                    },
                    0
                );
                return t;
            },
            onContextMenu: function(event) {
                var t = this;
                if($(e).data('limittextareaDisablecontextmenu') === true) {
                    event.preventDefault();
                } else {
                    window.setTimeout(
                        function() {
                            $(e).trigger('afterContextMenu');
                        },
                        0
                    );
                }
                return t;
            },
            onCut: function(event) {
                var t = this;
                window.setTimeout(
                    function() {
                        $(e).trigger('afterCut');
                    },
                    0
                );
                return t;
            },
            onPaste: function(event) {
                var t = this;
                window.setTimeout(
                    function() {
                        t.characterCount();
                    },
                    0
                );
                window.setTimeout(
                    function() {
                        $(e).trigger('afterPaste');
                    },
                    0
                );
                return t;
            },
            onEmpty: function() {
                var t = this;
                $(e).data('limittextareaIsempty', true);
                window.setTimeout(
                    function() {
                        $(e).trigger('afterEmpty');
                    },
                    0
                );
                return t;
            },
            onNotEmpty: function() {
                var t = this;
                $(e).data('limittextareaIsempty', false);
                window.setTimeout(
                    function() {
                        $(e).trigger('afterNotEmpty');
                    },
                    0
                );
                return t;
            },
            onWithinLimit: function(event) {
                var t = this;
                $(e).data('limittextareaLimitreached', false);
                window.setTimeout(
                    function() {
                        $(e).trigger('afterWithinLimit');
                    },
                    0
                );
                return t;
            },
            onReachLimit: function(event) {
                var t = this;
                $(e).data('limittextareaLimitreached', true);
                $(e).val($(e).val().substr(0, $(e).data('limittextareaLimit')));
                if($(e).data('limittextareaCharactercount') > $(e).data('limittextareaLimit'))
                    $(e).data('limittextareaCharactercount', $(e).data('limittextareaLimit'));
                window.setTimeout(
                    function() {
                        $(e).trigger('afterReachLimit');
                    },
                    0
                );
                return t;
            },
            /* End Non Editable Events */
            /* Start Editable Events */
            afterInit: function() {
                var t = this;
                return t;
            },
            afterKeyDown: function(event) {
                var t = this;
                return t;
            },
            afterKeyUp: function(event) {
                var t = this;
                return t;
            },
            afterMouseDown: function(event) {
                var t = this;
                return t;
            },
            afterMouseUp: function(event) {
                var t = this;
                return t;
            },
            afterContextMenu: function(event) {
                var t = this;
                return t;
            },
            afterCut: function(event) {
                var t = this;
                return t;
            },
            afterPaste: function(event) {
                var t = this;
                return t;
            },
            afterEmpty: function(event) {
                var t = this;
                return t;
            },
            afterNotEmpty: function(event) {
                var t = this;
                return t;
            },
            afterWithinLimit: function(event) {
                var t = this;
                return t;
            },
            afterReachLimit: function(event) {
                var t = this;
                return t;
            },
            /* End Editable Events */
            /* End Events */

            /* Start Properties */
            characterCount: function(val) {
                var
                    t = this,
                    returns = t,
                    remaining = 0
                ;

                if(val) {
                    $(e).data('limittextareaCharactercount', val);
                } else {
                    $(e).data('limittextareaCharactercount', $(e).val().length);
                    returns = $(e).data('limittextareaCharactercount');

                    if($(e).data('limittextareaCharactercount') == 0) {
                        $(e).trigger('empty');
                    } else {
                        $(e).trigger('notempty');
                    }

                    if($(e).data('limittextareaLimit')) {
                        if(parseInt($(e).data('limittextareaCharactercount')) + 1 > parseInt($(e).data('limittextareaLimit'))) {
                            $(e).trigger('reachlimit');
                        } else {
                            $(e).trigger('withinlimit');
                        }
                    } else {
                        //Error
                    }
                }

                if(t.initialized)
                    t.updateCounter();

                return returns;
            },
            limit: function(val) {
                return (val) ? $(e).data('limittextareaLimit', val) : $(e).data('limittextareaLimit');
            },
            disableContextMenu: function(val) {
                return (val) ? $(e).data('limittextareaDisablecontextmenu', val) : $(e).data('limittextareaDisablecontextmenu');
            }
            /* End Properties */
        };
        /* End Create Object */

        /* Start Set Options */
        if(options) {
            Object.keys(options).each(
                function(key) {
                    var value = options[key];
                    if($.inArray(key, overridable) >= 0) {
                        obj[key] = options[key];
                    } else if($.inArray(key, settable) >= 0) {
                        obj[key](value);
                    }
                }
            );
        }
        /* End Set Options */

        /* Start Set Event Listeners */
        $(e).on('keydown', function(event) { setKeyState(event.keyCode, true); obj.onKeyDown(event).characterCount(); });
        $(e).on('keyup', function(event) { setKeyState(event.keyCode);obj.onKeyUp(event).characterCount(); });
        $(e).on('mouseDown', function(event) { obj.afterKeyUp(event).characterCount(); });
        $(e).on('mouseup', function(event) { obj.onMouseUp(event).characterCount(); });
        $(e).on('contextmenu', function(event) { obj.onContextMenu(event).characterCount(); });
        $(e).on('cut', function(event) { obj.onCut(event).characterCount(); });
        $(e).on('paste', function(event) { obj.onPaste(event).characterCount(); });

        $(e).on('empty', function(event) { obj.onEmpty(event); });
        $(e).on('notempty', function(event) { obj.onNotEmpty(event); });
        $(e).on('withinlimit', function(event) { obj.onWithinLimit(event); });
        $(e).on('reachlimit', function(event) { obj.onReachLimit(event); });

        $(e).on('afterKeyDown', function(event) { obj.afterKeyDown(event); });
        $(e).on('afterKeyUp', function(event) { obj.afterKeyUp(event); });
        $(e).on('afterMouseDown', function(event) { obj.afterKeyUp(event); });
        $(e).on('afterMouseUp', function(event) { obj.afterMouseUp(event); });
        $(e).on('afterContextMenu', function(event) { obj.afterContextMenu(event); });
        $(e).on('afterCut', function(event) { obj.afterCut(event); });
        $(e).on('afterPaste', function(event) { obj.afterPaste(event); });
        $(e).on('afterEmpty', function(event) { obj.afterEmpty(event); });
        $(e).on('afterNotEmpty', function(event) { obj.afterNotEmpty(event); });
        $(e).on('afterWithinLimit', function(event) { obj.afterWithinLimit(event); });
        $(e).on('afterReachLimit', function(event) { obj.afterReachLimit(event); });
        /* End Set Event Listeners */

        return obj;
    };
    /* End Objct Constructor */

    /* Start Add Method To jQuery */
    $.fn.extend({
        limitTextarea: function(options) {
            if(!$(this).data('limittextarea')) {
                var obj = new LimitTextarea(this, options);

                $(this).data('limittextarea', obj);

                obj.init();

                return this;
            } else {
                return $(this).data('limittextarea');
            }
        }
    });
    /* End Add Method To jQuery */
})(jQuery);
<|endoftext|>


<commit_before>function updateNavigationLinks(jobName) {
  var getPage = function() {
    if (window.location.hash.substr(0, 2) === '#!') {
      var hash = window.location.hash.substr(2);
      var fragments = hash.split('/');
      return fragments[1]
        ? '/' + fragments[1]
        : '';
    }
  };
  var jobs = $('#jobs').find('[data-job]');
  var selectedJob = jobs.filter('[data-job="' + jobName + '"]');
  var page = getPage();
  var prev = $.merge(selectedJob.prev(), jobs.last());
  var next = $.merge(selectedJob.next(), jobs.first());
  $('#previous-job').attr('href', '#!' + prev.attr('data-job') + page);
  $('#next-job').attr('href', '#!' + next.attr('data-job') + page);
}
<commit_msg>updating navigation links of all jobs in the list<commit_after>function updateNavigationLinks(jobName) {
  var getPage = function() {
    if (window.location.hash.substr(0, 2) === '#!') {
      var hash = window.location.hash.substr(2);
      var fragments = hash.split('/');
      return fragments[1]
        ? '/' + fragments[1]
        : '';
    }
  };
  var jobs = $('#jobs').find('[data-job]');
  var selectedJob = jobs.filter('[data-job="' + jobName + '"]');
  var page = getPage();
  var prev = $.merge(selectedJob.prev(), jobs.last());
  var next = $.merge(selectedJob.next(), jobs.first());
  jobs.each(function(index, item) {
    var link = $(item).find('a');
    $(link).attr('href', '#!' + $(item).attr('data-job') + page);
  });
  $('#previous-job').attr('href', '#!' + prev.attr('data-job') + page);
  $('#next-job').attr('href', '#!' + next.attr('data-job') + page);
}
<|endoftext|>


<commit_before>/**
 * ==  scripty2 fx ==
 * The scripty2 effects framework provides for time-based transformations of DOM elements
 * and arbitrary JavaScript objects. This is at the core of scripty2 and presents a refinement
 * of the visual effects framework of script.aculo.us 1.
 *
 * In practice [[S2.FX.Morph]] is most often used, which allows transitions from one
 * set of CSS style rules to another.
 *
 *
Features

 *
 * * CSS morphing engine:
 *   morph from one set of style properties to another, including
 *   support for all CSS length types (px, em, %, cm, pt, etc.)
 * * Extensive transition system
 *   for animation easing and special effects (e.g. bouncing)
 * * On supported browsers, uses browser-native visual effects (CSS Transitions),
 *   fully automatically with no change to your code required
 * * Auto-adjusts to differences in computing and rendering speed (drops frames as necessary)
 * * Limits the number of attempted frame renders to conserve CPU in fast computers
 * * Flexible OOP-based implementation allows for easy extension and hacks
 * * Effect queuing to run an effect after other effects have finished
 * * Chaining and
 *   default options:
 *   easy-to-use syntax for most use cases
 * * Reusable effect instances
 *   can be used with more than one DOM element
 * * User-definable callback functions at any point in the effect lifecycle
 * * Effects are abortable (either as-is or abort-to-end)
**/

/** section: scripty2 fx
 * S2.FX
 * This is the main effects namespace.
**/
S2.FX = (function(){
  var queues = [], globalQueue,
    heartbeat, activeEffects = 0;

  function beatOnDemand(dir){
    heartbeat[(activeEffects += dir) > 0 ? 'start' : 'stop']();
  }

  function renderQueues(){
    queues.invoke('render', heartbeat.getTimestamp());
  }

  function initialize(initialHeartbeat){
    if(globalQueue) return;
    queues.push(globalQueue = new S2.FX.Queue());
    S2.FX.DefaultOptions.queue = globalQueue;
    heartbeat = initialHeartbeat || new S2.FX.Heartbeat();

    document
      .observe('effect:heartbeat', renderQueues)
      .observe('effect:queued',    beatOnDemand.curry(1))
      .observe('effect:dequeued',  beatOnDemand.curry(-1));
  }

  return {
    initialize: initialize,
    getQueues: function(){ return queues; },
    addQueue: function(queue){ queues.push(queue); },
    getHeartbeat: function(){ return heartbeat; },
    setHeartbeat: function(newHeartbeat){
      heartbeat = newHeartbeat;
    }
  }
})();

Object.extend(S2.FX, {
  DefaultOptions: {
    transition: 'sinusoidal',
    position:   'parallel',
    fps:        60,
    duration:   .2
  },

  elementDoesNotExistError: {
    name: 'ElementDoesNotExistError',
    message: 'The specified DOM element does not exist, but is required for this effect to operate'
  },

  parseOptions: function(options) {
    if (Object.isNumber(options))
      options = { duration: options };
    else if (Object.isFunction(options))
      options = { after: options };
    else if (Object.isString(options))
      options = { duration: options == 'slow' ? 1 : options == 'fast' ? .1 : .2 };

    return options;
  }
});

/**
 *  class S2.FX.Base
**/
S2.FX.Base = Class.create({
  /**
   *  new S2.FX.Base([options])
   *  - options (Number | Function | Object): options for the effect.
   *
   *  Base calls for all effects. Subclasses need to define the `update` instance
   *  method for it to be useful. scripty2 currently provides one subclass implementation
   *  for effects based on DOM elements, [[S2.FX.Element]].
   *
   *
Effect options

   *
   *  There are serveral ways the options argument can be used:
   *
   *      new S2.FX.Base({ duration: 3, transition: 'spring' });
   *      new S2.FX.Base(function(){})   // shortcut for { after: function(){} }
   *      new S2.FX.Base(3);             // shortcut for { duration: 3 }
   *      new S2.FX.Base('slow');        // shortcut for { duration: 1 }
   *      new S2.FX.Base('fast');        // shortcut for { duration: .1 }
   *
   *  The following options are recognized:
   *
   *  * `duration`: duration in seconds, defaults to 0.2 (a fifth of a second). This
   *     speed is based on the value Mac OS X uses for interface effects.
   *  * `transition`: Function reference or String with a property name from [[S2.FX.Transitions]].
   *    Sets the transition method for easing and other special effects.
   *  * `before`: Function to be executed before the first frame of the effect is rendered.
   *  * `after`: Function to be executed after the effect has finished.
   *  * `beforeUpdate`: Function to be executed before each frame is rendered. This function
   *    is given two parameters: the effect instance and the current position (between 0 and 1).
   *  * `afterUpdate`: Function to be executed after each frame is renedred. This function
   *    is given two parameters: the effect instance and the current position (between 0 and 1).
   *  * `fps`: The maximum number of frames per second. Ensures that no more than `options.fps`
   *    frames per second are rendered, even if there's enough computation resources available.
   *    This can be used to make CPU-intensive effects use fewer resources.
   *  * `queue`: Specify a [[S2.FX.Queue]] to be used for the effect.
   *  * `position`: Position within the specified queue, `parallel` (start immediately, default) or `end`
   *    (queue up until the last effect in the queue is finished)
   *
   *  The effect won't start immediately, it will merely be initialized.
   *  To start the effect, call [[S2.FX.Base#play]].
  **/
  initialize: function(options) {
    S2.FX.initialize();
    this.updateWithoutWrappers = this.update;

    if(options && options.queue && !S2.FX.getQueues().include(options.queue))
      S2.FX.addQueue(options.queue);

    this.setOptions(options);
    this.duration = this.options.duration*1000;
    this.state = 'idle';

    ['after','before'].each(function(method) {
      this[method] = function(method) {
        method(this);
        return this;
      }
    }, this);
  },

  setOptions: function(options) {
    options = S2.FX.parseOptions(options);

    if (!this.options) {
      this.options = Object.extend(Object.extend({},S2.FX.DefaultOptions), options);
      if(options.tween) this.options.transition = options.tween;
    }

    if (this.options.beforeUpdate || this.options.afterUpdate) {
      this.update = this.updateWithoutWrappers.wrap( function(proceed,position){
        if (this.options.beforeUpdate) this.options.beforeUpdate(this, position);
        proceed(position);
        if (this.options.afterUpdate) this.options.afterUpdate(this, position);
      }.bind(this));
    }
    if(this.options.transition === false)
      this.options.transition = S2.FX.Transitions.linear;
    this.options.transition = Object.propertize(this.options.transition, S2.FX.Transitions);
  },

  /**
   *  S2.FX.Base#play([options]) -> S2.FX.Base
   *  - options: Effect options, see above.
   *
   *  Starts playing the effect.
   *  Returns the effect.
  **/
  play: function(options) {
    this.setOptions(options);
    this.frameCount = 0;
    this.options.queue.add(this);
    this.maxFrames = this.options.fps * this.duration / 1000;
    return this;
  },

  /**
   *  S2.FX.Base#render(timestamp) -> undefined
   *  - timestamp (Date): point in time, normally the current time
   *
   *  Renders the effect, and calls the before/after functions when necessary.
   *  This method also switches the state of the effect from `idle` to `running` when
   *  the first frame is rendered, and from `running` to `finished` after the last frame
   *  is rendered.
  **/
  render: function(timestamp) {
    if (timestamp >= this.startsAt) {
      if (this.state == 'idle') {
        if (this.options.before) this.options.before(this);
        if (this.setup) this.setup();
        this.state = 'running';
        this.update(this.options.transition(0));
        this.frameCount++;
        return this;
      }
      if (timestamp >= this.endsAt && !(this.state == 'finished')) {
        this.update(this.options.transition(1));
        if (this.teardown) this.teardown();
        if (this.options.after) this.options.after(this);
        this.state = 'finished';
      } else if (this.state == 'running') {
        var position = 1 - (this.endsAt - timestamp) / this.duration;
        if ((this.maxFrames * position).floor() > this.frameCount) {
          this.update(this.options.transition(position));
          this.frameCount++;
        }
      }
    }
    return this;
  },

  /**
   *  S2.FX.Base#cancel([after]) -> undefined
   *  - after (Boolean): if true, run the after method (if defined), defaults to false
   *
   *  Calling `cancel()` immediately halts execution of the effect, and calls the `teardown`
   *  method if defined.
  **/
  cancel: function(after) {
    if(!this.state == 'running') return;
    if (this.teardown) this.teardown();
    if (after && this.options.after) this.options.after(this);
    this.state = 'finished';
  },

  /**
   *  S2.FX.Base#finish() -> undefined
   *
   *  Immediately render the last frame and halt execution of the effect
   *  and call the `teardown`method if defined.
  **/
  finish: function(after) {
    if(!this.state == 'running') return;
    this.update(this.options.transition(1));
    this.cancel(true);
  },

  /**
   *  S2.FX.Base#inspect() -> String
   *
   *  Returns the debug-oriented string representation of an effect.
  **/
  inspect: function() {
    return '#<S2.FX:' + [this.state, this.startsAt, this.endsAt].inspect() + '>';
  }
});

/**
 * class S2.FX.Element < S2.FX.Base
 * Base class for effects that change DOM elements. This is the base class for
 * the most important effects implementation [[S2.FX.Morph]], but can be used
 * as a base class for non-CSS based effects too.
**/
S2.FX.Element = Class.create(S2.FX.Base, {
  /**
   *  new S2.FX.Element(element[, options])
   *  - element (Object | String): DOM element or element ID
   *  - options (Number | Function | Object): options for the effect.
   *
   *  See [[S2.FX.Base]] for a description of the `options` argument.
  **/
  initialize: function($super, element, options) {
    if(!(this.element = $(element)))
      throw(S2.FX.elementDoesNotExistError);
    this.operators = [];
    return $super(options);
  },

  /**
   *  S2.FX.Element#animate(operator[, args...]) -> undefined
   *  - operator (String): lowercase name of an [[S2.FX.Operator]]
   *
   *  Starts an animation by using a [[S2.FX.Operator]] on the element
   *  that is associated with the effect.
   *
   *  The rest of the arguments are passed to Operators' constructor.
   *  This method is intended to be called in the `setup` instance method
   *  of subclasses, for example:
   *
   *      // setup method from S2.FX.Style
   *      setup: function() {
   *        this.animate('style', this.element, { style: this.options.style });
   *      }
  **/
  animate: function() {
    var args = $A(arguments), operator =  args.shift();
    operator = operator.charAt(0).toUpperCase() + operator.substring(1);
    this.operators.push(new S2.FX.Operators[operator](this, args[0], args[1] || {}));
  },

  /**
   *  S2.FX.Element#play([element[, options]]) -> S2.FX.Base
   *  - element (Object | String): DOM element or element ID
   *  - options (Number | Function | Object): options for the effect.
   *
   *  Starts playing the element effect. The optional `element` argument can
   *  be used to reuse this instance on a different element than for
   *  which it was used originally.
  **/
  play: function($super, element, options) {
    if (element) this.element = $(element);
    return $super(options);
  },

  update: function(position) {
    this.operators.invoke('render', position);
  }
});
<commit_msg>S2.FX.parseOptions always return an Object<commit_after>/**
 * ==  scripty2 fx ==
 * The scripty2 effects framework provides for time-based transformations of DOM elements
 * and arbitrary JavaScript objects. This is at the core of scripty2 and presents a refinement
 * of the visual effects framework of script.aculo.us 1.
 *
 * In practice [[S2.FX.Morph]] is most often used, which allows transitions from one
 * set of CSS style rules to another.
 *
 *
Features

 *
 * * CSS morphing engine:
 *   morph from one set of style properties to another, including
 *   support for all CSS length types (px, em, %, cm, pt, etc.)
 * * Extensive transition system
 *   for animation easing and special effects (e.g. bouncing)
 * * On supported browsers, uses browser-native visual effects (CSS Transitions),
 *   fully automatically with no change to your code required
 * * Auto-adjusts to differences in computing and rendering speed (drops frames as necessary)
 * * Limits the number of attempted frame renders to conserve CPU in fast computers
 * * Flexible OOP-based implementation allows for easy extension and hacks
 * * Effect queuing to run an effect after other effects have finished
 * * Chaining and
 *   default options:
 *   easy-to-use syntax for most use cases
 * * Reusable effect instances
 *   can be used with more than one DOM element
 * * User-definable callback functions at any point in the effect lifecycle
 * * Effects are abortable (either as-is or abort-to-end)
**/

/** section: scripty2 fx
 * S2.FX
 * This is the main effects namespace.
**/
S2.FX = (function(){
  var queues = [], globalQueue,
    heartbeat, activeEffects = 0;

  function beatOnDemand(dir){
    heartbeat[(activeEffects += dir) > 0 ? 'start' : 'stop']();
  }

  function renderQueues(){
    queues.invoke('render', heartbeat.getTimestamp());
  }

  function initialize(initialHeartbeat){
    if(globalQueue) return;
    queues.push(globalQueue = new S2.FX.Queue());
    S2.FX.DefaultOptions.queue = globalQueue;
    heartbeat = initialHeartbeat || new S2.FX.Heartbeat();

    document
      .observe('effect:heartbeat', renderQueues)
      .observe('effect:queued',    beatOnDemand.curry(1))
      .observe('effect:dequeued',  beatOnDemand.curry(-1));
  }

  return {
    initialize: initialize,
    getQueues: function(){ return queues; },
    addQueue: function(queue){ queues.push(queue); },
    getHeartbeat: function(){ return heartbeat; },
    setHeartbeat: function(newHeartbeat){
      heartbeat = newHeartbeat;
    }
  }
})();

Object.extend(S2.FX, {
  DefaultOptions: {
    transition: 'sinusoidal',
    position:   'parallel',
    fps:        60,
    duration:   .2
  },

  elementDoesNotExistError: {
    name: 'ElementDoesNotExistError',
    message: 'The specified DOM element does not exist, but is required for this effect to operate'
  },

  parseOptions: function(options) {
    if (Object.isNumber(options))
      options = { duration: options };
    else if (Object.isFunction(options))
      options = { after: options };
    else if (Object.isString(options))
      options = { duration: options == 'slow' ? 1 : options == 'fast' ? .1 : .2 };

    return options || {};
  }
});

/**
 *  class S2.FX.Base
**/
S2.FX.Base = Class.create({
  /**
   *  new S2.FX.Base([options])
   *  - options (Number | Function | Object): options for the effect.
   *
   *  Base calls for all effects. Subclasses need to define the `update` instance
   *  method for it to be useful. scripty2 currently provides one subclass implementation
   *  for effects based on DOM elements, [[S2.FX.Element]].
   *
   *
Effect options

   *
   *  There are serveral ways the options argument can be used:
   *
   *      new S2.FX.Base({ duration: 3, transition: 'spring' });
   *      new S2.FX.Base(function(){})   // shortcut for { after: function(){} }
   *      new S2.FX.Base(3);             // shortcut for { duration: 3 }
   *      new S2.FX.Base('slow');        // shortcut for { duration: 1 }
   *      new S2.FX.Base('fast');        // shortcut for { duration: .1 }
   *
   *  The following options are recognized:
   *
   *  * `duration`: duration in seconds, defaults to 0.2 (a fifth of a second). This
   *     speed is based on the value Mac OS X uses for interface effects.
   *  * `transition`: Function reference or String with a property name from [[S2.FX.Transitions]].
   *    Sets the transition method for easing and other special effects.
   *  * `before`: Function to be executed before the first frame of the effect is rendered.
   *  * `after`: Function to be executed after the effect has finished.
   *  * `beforeUpdate`: Function to be executed before each frame is rendered. This function
   *    is given two parameters: the effect instance and the current position (between 0 and 1).
   *  * `afterUpdate`: Function to be executed after each frame is renedred. This function
   *    is given two parameters: the effect instance and the current position (between 0 and 1).
   *  * `fps`: The maximum number of frames per second. Ensures that no more than `options.fps`
   *    frames per second are rendered, even if there's enough computation resources available.
   *    This can be used to make CPU-intensive effects use fewer resources.
   *  * `queue`: Specify a [[S2.FX.Queue]] to be used for the effect.
   *  * `position`: Position within the specified queue, `parallel` (start immediately, default) or `end`
   *    (queue up until the last effect in the queue is finished)
   *
   *  The effect won't start immediately, it will merely be initialized.
   *  To start the effect, call [[S2.FX.Base#play]].
  **/
  initialize: function(options) {
    S2.FX.initialize();
    this.updateWithoutWrappers = this.update;

    if(options && options.queue && !S2.FX.getQueues().include(options.queue))
      S2.FX.addQueue(options.queue);

    this.setOptions(options);
    this.duration = this.options.duration*1000;
    this.state = 'idle';

    ['after','before'].each(function(method) {
      this[method] = function(method) {
        method(this);
        return this;
      }
    }, this);
  },

  setOptions: function(options) {
    options = S2.FX.parseOptions(options);

    if (!this.options) {
      this.options = Object.extend(Object.extend({},S2.FX.DefaultOptions), options);
      if(options.tween) this.options.transition = options.tween;
    }

    if (this.options.beforeUpdate || this.options.afterUpdate) {
      this.update = this.updateWithoutWrappers.wrap( function(proceed,position){
        if (this.options.beforeUpdate) this.options.beforeUpdate(this, position);
        proceed(position);
        if (this.options.afterUpdate) this.options.afterUpdate(this, position);
      }.bind(this));
    }
    if(this.options.transition === false)
      this.options.transition = S2.FX.Transitions.linear;
    this.options.transition = Object.propertize(this.options.transition, S2.FX.Transitions);
  },

  /**
   *  S2.FX.Base#play([options]) -> S2.FX.Base
   *  - options: Effect options, see above.
   *
   *  Starts playing the effect.
   *  Returns the effect.
  **/
  play: function(options) {
    this.setOptions(options);
    this.frameCount = 0;
    this.options.queue.add(this);
    this.maxFrames = this.options.fps * this.duration / 1000;
    return this;
  },

  /**
   *  S2.FX.Base#render(timestamp) -> undefined
   *  - timestamp (Date): point in time, normally the current time
   *
   *  Renders the effect, and calls the before/after functions when necessary.
   *  This method also switches the state of the effect from `idle` to `running` when
   *  the first frame is rendered, and from `running` to `finished` after the last frame
   *  is rendered.
  **/
  render: function(timestamp) {
    if (timestamp >= this.startsAt) {
      if (this.state == 'idle') {
        if (this.options.before) this.options.before(this);
        if (this.setup) this.setup();
        this.state = 'running';
        this.update(this.options.transition(0));
        this.frameCount++;
        return this;
      }
      if (timestamp >= this.endsAt && !(this.state == 'finished')) {
        this.update(this.options.transition(1));
        if (this.teardown) this.teardown();
        if (this.options.after) this.options.after(this);
        this.state = 'finished';
      } else if (this.state == 'running') {
        var position = 1 - (this.endsAt - timestamp) / this.duration;
        if ((this.maxFrames * position).floor() > this.frameCount) {
          this.update(this.options.transition(position));
          this.frameCount++;
        }
      }
    }
    return this;
  },

  /**
   *  S2.FX.Base#cancel([after]) -> undefined
   *  - after (Boolean): if true, run the after method (if defined), defaults to false
   *
   *  Calling `cancel()` immediately halts execution of the effect, and calls the `teardown`
   *  method if defined.
  **/
  cancel: function(after) {
    if(!this.state == 'running') return;
    if (this.teardown) this.teardown();
    if (after && this.options.after) this.options.after(this);
    this.state = 'finished';
  },

  /**
   *  S2.FX.Base#finish() -> undefined
   *
   *  Immediately render the last frame and halt execution of the effect
   *  and call the `teardown`method if defined.
  **/
  finish: function(after) {
    if(!this.state == 'running') return;
    this.update(this.options.transition(1));
    this.cancel(true);
  },

  /**
   *  S2.FX.Base#inspect() -> String
   *
   *  Returns the debug-oriented string representation of an effect.
  **/
  inspect: function() {
    return '#<S2.FX:' + [this.state, this.startsAt, this.endsAt].inspect() + '>';
  }
});

/**
 * class S2.FX.Element < S2.FX.Base
 * Base class for effects that change DOM elements. This is the base class for
 * the most important effects implementation [[S2.FX.Morph]], but can be used
 * as a base class for non-CSS based effects too.
**/
S2.FX.Element = Class.create(S2.FX.Base, {
  /**
   *  new S2.FX.Element(element[, options])
   *  - element (Object | String): DOM element or element ID
   *  - options (Number | Function | Object): options for the effect.
   *
   *  See [[S2.FX.Base]] for a description of the `options` argument.
  **/
  initialize: function($super, element, options) {
    if(!(this.element = $(element)))
      throw(S2.FX.elementDoesNotExistError);
    this.operators = [];
    return $super(options);
  },

  /**
   *  S2.FX.Element#animate(operator[, args...]) -> undefined
   *  - operator (String): lowercase name of an [[S2.FX.Operator]]
   *
   *  Starts an animation by using a [[S2.FX.Operator]] on the element
   *  that is associated with the effect.
   *
   *  The rest of the arguments are passed to Operators' constructor.
   *  This method is intended to be called in the `setup` instance method
   *  of subclasses, for example:
   *
   *      // setup method from S2.FX.Style
   *      setup: function() {
   *        this.animate('style', this.element, { style: this.options.style });
   *      }
  **/
  animate: function() {
    var args = $A(arguments), operator =  args.shift();
    operator = operator.charAt(0).toUpperCase() + operator.substring(1);
    this.operators.push(new S2.FX.Operators[operator](this, args[0], args[1] || {}));
  },

  /**
   *  S2.FX.Element#play([element[, options]]) -> S2.FX.Base
   *  - element (Object | String): DOM element or element ID
   *  - options (Number | Function | Object): options for the effect.
   *
   *  Starts playing the element effect. The optional `element` argument can
   *  be used to reuse this instance on a different element than for
   *  which it was used originally.
  **/
  play: function($super, element, options) {
    if (element) this.element = $(element);
    return $super(options);
  },

  update: function(position) {
    this.operators.invoke('render', position);
  }
});
<|endoftext|>


<commit_before>var LastViewedPages = (function() {
  function LastViewedPages(options) {
    options = options || {};
    this.pages = options.pages || [];
    this.max = options.max || 10;
    this.localStorageId = 'lastViewedPages';

    localStorageSetObject(this.localStorageId, this.pages);
  }

  LastViewedPages.prototype.all = function() {
    return this.pages;
  };

  LastViewedPages.prototype.addPage = function(newPage) {
    var pages = this.pages.filter(function(page) {
      return JSON.stringify(page) != JSON.stringify(newPage);
    });

    pages.unshift(newPage);

    pages = pages.slice(0, this.max);

    this.pages = pages;

    this._saveToLocalStorage();
  };

  LastViewedPages.prototype._saveToLocalStorage = function() {
    localStorageSetObject(this.localStorageId, this.pages);
  };

  return LastViewedPages;
})();
<commit_msg>comments<commit_after>var LastViewedPages = (function() {
  function LastViewedPages(options) {
    options = options || {};
    this.pages = options.pages || [];
    this.max = options.max || 10;
    this.localStorageId = 'lastViewedPages';

    localStorageSetObject(this.localStorageId, this.pages);
  }

  LastViewedPages.prototype.all = function() {
    return this.pages;
  };

  LastViewedPages.prototype.addPage = function(newPage) {
    // filter way pages that are already in the list
    var pages = this.pages.filter(function(page) {
      return JSON.stringify(page) != JSON.stringify(newPage);
    });

    // prepend the new page
    pages.unshift(newPage);

    // make sure the list is not too long
    pages = pages.slice(0, this.max);

    // save the pages
    this.pages = pages;

    // save them to local storage
    this._saveToLocalStorage();
  };

  LastViewedPages.prototype._saveToLocalStorage = function() {
    localStorageSetObject(this.localStorageId, this.pages);
  };

  return LastViewedPages;
})();
<|endoftext|>


<commit_before>
import * as dom from './dom.js';

export {eventManager};

window.Handsontable = window.Handsontable || {};
// used to debug memory leaks
Handsontable.countEventManagerListeners = 0;
// support for older versions of Handsontable
Handsontable.eventManager = eventManager;

function eventManager(instance) {
  if (!instance) {
    throw new Error ('instance not defined');
  }

  if (!instance.eventListeners) {
    instance.eventListeners = [];
  }

  function extendEvent(event) {
    var
      componentName = 'HOT-TABLE',
      isHotTableSpotted,
      fromElement,
      realTarget,
      target,
      len;

    event.isTargetWebComponent = false;
    event.realTarget = event.target;

    if (!Handsontable.eventManager.isHotTableEnv) {
      return event;
    }
    event = dom.polymerWrap(event);
    len = event.path.length;

    while (len --) {
      if (event.path[len].nodeName === componentName) {
        isHotTableSpotted = true;

      } else if (isHotTableSpotted && event.path[len].shadowRoot) {
        target = event.path[len];

        break;
      }
      if (len === 0 && !target) {
        target = event.path[len];
      }
    }
    if (!target) {
      target = event.target;
    }
    event.isTargetWebComponent = true;

    if (dom.isWebComponentSupportedNatively()) {
      event.realTarget = event.srcElement || event.toElement;

    } else if (instance instanceof Handsontable.Core || instance instanceof Walkontable) {
      // Polymer doesn't support `event.target` property properly we must emulate it ourselves
      if (instance instanceof Handsontable.Core) {
        fromElement = instance.view.wt.wtTable.TABLE;

      } else if (instance instanceof Walkontable) {
        // .wtHider
        fromElement = instance.wtTable.TABLE.parentNode.parentNode;
      }
      realTarget = dom.closest(event.target, [componentName], fromElement);

      if (realTarget) {
        event.realTarget = fromElement.querySelector(componentName) || event.target;
      } else {
        event.realTarget = event.target;
      }
    }

    Object.defineProperty(event, 'target', {
      get: function() {
        return dom.polymerWrap(target);
      },
      enumerable: true,
      configurable: true
    });

    return event;
  }

  /**
   * Add Event
   *
   * @param {Element} element
   * @param {String} event
   * @param {Function} callback
   * @returns {Function} Returns function which you can easily call to remove that event
   */
  function addEvent(element, event, callback) {
    var callbackProxy;

    callbackProxy = function callbackProxy(event) {
      if (event.target == void 0 && event.srcElement != void 0) {
        if (event.definePoperty) {
          event.definePoperty('target', {
            value: event.srcElement
          });
        } else {
          event.target = event.srcElement;
        }
      }
      if (event.preventDefault == void 0) {
        if (event.definePoperty) {
          event.definePoperty('preventDefault', {
            value: function() {
              this.returnValue = false;
            }
          });
        } else {
          event.preventDefault = function () {
            this.returnValue = false;
          };
        }
      }
      event = extendEvent(event);

      callback.call(this, event);
    };

    instance.eventListeners.push({
      element: element,
      event: event,
      callback: callback,
      callbackProxy: callbackProxy
    });

    if (window.addEventListener) {
      element.addEventListener(event, callbackProxy, false);
    } else {
      element.attachEvent('on' + event, callbackProxy);
    }
    Handsontable.countEventManagerListeners ++;

    return function _removeEvent() {
      removeEvent(element, event, callback);
    };
  }

  /**
   * Remove event
   *
   * @param {Element} element
   * @param {String} event
   * @param {Function} callback
   */
  function removeEvent(element, event, callback){
    var len = instance.eventListeners.length,
      tmpEvent;

    while (len--) {
      tmpEvent = instance.eventListeners[len];

      if (tmpEvent.event == event && tmpEvent.element == element) {
        if (callback && callback != tmpEvent.callback) {
          continue;
        }
        instance.eventListeners.splice(len, 1);

        if (tmpEvent.element.removeEventListener) {
          tmpEvent.element.removeEventListener(tmpEvent.event, tmpEvent.callbackProxy, false);
        } else {
          tmpEvent.element.detachEvent('on' + tmpEvent.event, tmpEvent.callbackProxy);
        }
        Handsontable.countEventManagerListeners --;
      }
    }
  }

  /**
   * Clear all events
   */
  function clearEvents() {
    var len = instance.eventListeners.length,
      event;

    while (len--) {
      event = instance.eventListeners[len];

      if (event) {
        removeEvent(event.element, event.event, event.callback);
      }
    }
  }

  /**
   * Trigger event
   *
   * @param {Element} element
   * @param {String} type
   */
  function fireEvent(element, type) {
    var options = {
      bubbles: true,
      cancelable: (type !== "mousemove"),
      view: window,
      detail: 0,
      screenX: 0,
      screenY: 0,
      clientX: 1,
      clientY: 1,
      ctrlKey: false,
      altKey: false,
      shiftKey: false,
      metaKey: false,
      button: 0,
      relatedTarget: undefined
    };

    var event;
    if ( document.createEvent ) {
      event = document.createEvent("MouseEvents");
      event.initMouseEvent(type, options.bubbles, options.cancelable,
        options.view, options.detail,
        options.screenX, options.screenY, options.clientX, options.clientY,
        options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
        options.button, options.relatedTarget || document.body.parentNode);

    } else {
      event = document.createEventObject();
    }

    if (element.dispatchEvent) {
      element.dispatchEvent(event);
    } else {
      element.fireEvent('on' + type, event);
    }
  }

  return {
    addEventListener: addEvent,
    removeEventListener: removeEvent,
    clear: clearEvents,
    fireEvent: fireEvent
  };
}
<commit_msg>Fix null MouseEvent path on safari<commit_after>
import * as dom from './dom.js';

export {eventManager};

window.Handsontable = window.Handsontable || {};
// used to debug memory leaks
Handsontable.countEventManagerListeners = 0;
// support for older versions of Handsontable
Handsontable.eventManager = eventManager;

function eventManager(instance) {
  if (!instance) {
    throw new Error ('instance not defined');
  }

  if (!instance.eventListeners) {
    instance.eventListeners = [];
  }

  function extendEvent(event) {
    var
      componentName = 'HOT-TABLE',
      isHotTableSpotted,
      fromElement,
      realTarget,
      target,
      len;

    event.isTargetWebComponent = false;
    event.realTarget = event.target;

    if (!Handsontable.eventManager.isHotTableEnv) {
      return event;
    }
    event = dom.polymerWrap(event);
    len = event.path ? event.path.length : 0;

    while (len --) {
      if (event.path[len].nodeName === componentName) {
        isHotTableSpotted = true;

      } else if (isHotTableSpotted && event.path[len].shadowRoot) {
        target = event.path[len];

        break;
      }
      if (len === 0 && !target) {
        target = event.path[len];
      }
    }
    if (!target) {
      target = event.target;
    }
    event.isTargetWebComponent = true;

    if (dom.isWebComponentSupportedNatively()) {
      event.realTarget = event.srcElement || event.toElement;

    } else if (instance instanceof Handsontable.Core || instance instanceof Walkontable) {
      // Polymer doesn't support `event.target` property properly we must emulate it ourselves
      if (instance instanceof Handsontable.Core) {
        fromElement = instance.view.wt.wtTable.TABLE;

      } else if (instance instanceof Walkontable) {
        // .wtHider
        fromElement = instance.wtTable.TABLE.parentNode.parentNode;
      }
      realTarget = dom.closest(event.target, [componentName], fromElement);

      if (realTarget) {
        event.realTarget = fromElement.querySelector(componentName) || event.target;
      } else {
        event.realTarget = event.target;
      }
    }

    Object.defineProperty(event, 'target', {
      get: function() {
        return dom.polymerWrap(target);
      },
      enumerable: true,
      configurable: true
    });

    return event;
  }

  /**
   * Add Event
   *
   * @param {Element} element
   * @param {String} event
   * @param {Function} callback
   * @returns {Function} Returns function which you can easily call to remove that event
   */
  function addEvent(element, event, callback) {
    var callbackProxy;

    callbackProxy = function callbackProxy(event) {
      if (event.target == void 0 && event.srcElement != void 0) {
        if (event.definePoperty) {
          event.definePoperty('target', {
            value: event.srcElement
          });
        } else {
          event.target = event.srcElement;
        }
      }
      if (event.preventDefault == void 0) {
        if (event.definePoperty) {
          event.definePoperty('preventDefault', {
            value: function() {
              this.returnValue = false;
            }
          });
        } else {
          event.preventDefault = function () {
            this.returnValue = false;
          };
        }
      }
      event = extendEvent(event);

      callback.call(this, event);
    };

    instance.eventListeners.push({
      element: element,
      event: event,
      callback: callback,
      callbackProxy: callbackProxy
    });

    if (window.addEventListener) {
      element.addEventListener(event, callbackProxy, false);
    } else {
      element.attachEvent('on' + event, callbackProxy);
    }
    Handsontable.countEventManagerListeners ++;

    return function _removeEvent() {
      removeEvent(element, event, callback);
    };
  }

  /**
   * Remove event
   *
   * @param {Element} element
   * @param {String} event
   * @param {Function} callback
   */
  function removeEvent(element, event, callback){
    var len = instance.eventListeners.length,
      tmpEvent;

    while (len--) {
      tmpEvent = instance.eventListeners[len];

      if (tmpEvent.event == event && tmpEvent.element == element) {
        if (callback && callback != tmpEvent.callback) {
          continue;
        }
        instance.eventListeners.splice(len, 1);

        if (tmpEvent.element.removeEventListener) {
          tmpEvent.element.removeEventListener(tmpEvent.event, tmpEvent.callbackProxy, false);
        } else {
          tmpEvent.element.detachEvent('on' + tmpEvent.event, tmpEvent.callbackProxy);
        }
        Handsontable.countEventManagerListeners --;
      }
    }
  }

  /**
   * Clear all events
   */
  function clearEvents() {
    var len = instance.eventListeners.length,
      event;

    while (len--) {
      event = instance.eventListeners[len];

      if (event) {
        removeEvent(event.element, event.event, event.callback);
      }
    }
  }

  /**
   * Trigger event
   *
   * @param {Element} element
   * @param {String} type
   */
  function fireEvent(element, type) {
    var options = {
      bubbles: true,
      cancelable: (type !== "mousemove"),
      view: window,
      detail: 0,
      screenX: 0,
      screenY: 0,
      clientX: 1,
      clientY: 1,
      ctrlKey: false,
      altKey: false,
      shiftKey: false,
      metaKey: false,
      button: 0,
      relatedTarget: undefined
    };

    var event;
    if ( document.createEvent ) {
      event = document.createEvent("MouseEvents");
      event.initMouseEvent(type, options.bubbles, options.cancelable,
        options.view, options.detail,
        options.screenX, options.screenY, options.clientX, options.clientY,
        options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
        options.button, options.relatedTarget || document.body.parentNode);

    } else {
      event = document.createEventObject();
    }

    if (element.dispatchEvent) {
      element.dispatchEvent(event);
    } else {
      element.fireEvent('on' + type, event);
    }
  }

  return {
    addEventListener: addEvent,
    removeEventListener: removeEvent,
    clear: clearEvents,
    fireEvent: fireEvent
  };
}
<|endoftext|>


<commit_before>const electron = require('electron')
// Module to control application life.
const app = electron.app
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow

const path = require('path')
const url = require('url')

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow

function createWindow () {
  // Create the browser window.
  mainWindow = new BrowserWindow({width: 800, height: 620})

  // and load the index.html of the app.
  mainWindow.loadURL(url.format({
    pathname: path.join(__dirname, 'index.html'),
    protocol: 'file:',
    slashes: true
  }))

  // Open the DevTools.
  // mainWindow.webContents.openDevTools()

  // Emitted when the window is closed.
  mainWindow.on('closed', function () {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null
  })
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)

// Quit when all windows are closed.
app.on('window-all-closed', function () {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', function () {
  // On OS X it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (mainWindow === null) {
    createWindow()
  }
})

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
<commit_msg>Enlarge the window on Windows<commit_after>const electron = require('electron')
// Module to control application life.
const app = electron.app
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow

const path = require('path')
const url = require('url')
const os = require('os');

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow

function createWindow() {
    // Create the browser window.
    var isWin = (os.platform() === 'win32');

    height = 640
    if (isWin) {
        height += 20
    }
    mainWindow = new BrowserWindow({ width: 800, height: height })

    // and load the index.html of the app.
    mainWindow.loadURL(url.format({
        pathname: path.join(__dirname, 'index.html'),
        protocol: 'file:',
        slashes: true
    }))

    // Open the DevTools.
    // mainWindow.webContents.openDevTools()

    // Emitted when the window is closed.
    mainWindow.on('closed', function () {
        // Dereference the window object, usually you would store windows
        // in an array if your app supports multi windows, this is the time
        // when you should delete the corresponding element.
        mainWindow = null
    })
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)

// Quit when all windows are closed.
app.on('window-all-closed', function () {
    // On OS X it is common for applications and their menu bar
    // to stay active until the user quits explicitly with Cmd + Q
    if (process.platform !== 'darwin') {
        app.quit()
    }
})

app.on('activate', function () {
    // On OS X it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (mainWindow === null) {
        createWindow()
    }
})

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
<|endoftext|>


<commit_before>/*jshint expr:true*/
var T = require('../..');
var fs = T.fs;
var should = T.should;
var Batch = T.Batch;

describe('fs stub', function() {
  describe('#exists', function() {
    it('should detect stub', function(testDone) {
      var self = this;
      var batch = new Batch();

      batch.push(function(taskDone) {
        fs.exists(self.paths[0], function(exists) {
          should.equal(exists, false);
          taskDone();
        });
      });

      batch.end(function() {
        self.stubFile(self.paths[0]).make();
        fs.exists(self.paths[0], function(exists) {
          should.equal(exists, true);
          testDone();
        });
      });
    });
  });

  describe('#existsSync', function() {
    it('should detect stub', function(testDone) {
      should.equal(fs.existsSync(this.paths[0]), false);
      should.equal(fs.existsSync(this.paths[1]), false);
      this.stubFile(this.paths[0]).make();
      this.stubFile(this.paths[1]).make();
      should.equal(fs.existsSync(this.paths[0]), true);
      should.equal(fs.existsSync(this.paths[1]), true);
      testDone();
    });
  });

  describe('#writeFile', function() {
    it('should update fake file map', function(testDone) {
      var self = this;
      this.stubFile(this.paths[0]).make();
      fs.writeFile(this.paths[0], this.strings[0], function(err) {
        should.equal(err, null);
        fs.readFileSync(self.paths[0]).toString().should.equal(self.strings[0]);
        testDone();
      });
    });
  });

  describe('#writeFileSync', function() {
    it('should update fake file map', function(testDone) {
      this.stubFile(this.paths[0]).make();
      fs.writeFileSync(this.paths[0], this.strings[0]);
      fs.readFileSync(this.paths[0]).toString().should.equal(this.strings[0]);
      testDone();
    });
  });

  describe('#unlink', function() {
    it('should remove itself from parent readdir', function(testDone) {
      console.log('\x1B[33m<---------- INCOMPLETE'); testDone(); // TODO
    });

    it('should update #existsSync result', function(testDone) {
      console.log('\x1B[33m<---------- INCOMPLETE'); testDone(); // TODO
    });

    it('should remove itself from stub map', function(testDone) {
      console.log('\x1B[33m<---------- INCOMPLETE'); testDone(); // TODO
    });

    it('should call unlink on child stubs', function(testDone) {
      console.log('\x1B[33m<---------- INCOMPLETE'); testDone(); // TODO
    });
  });

  describe('#renameSync', function() {
    it('should copy readFile buffers', function(testDone) {
      console.log('\x1B[33m<---------- INCOMPLETE'); testDone(); // TODO
    });

    it('should rename copied dir descendants', function(testDone) {
      this.stubFile('/root').readdir([
        this.stubFile('/root/a').readdir([
          this.stubFile('/root/a/index.js')
        ])
      ]).make();
      fs.renameSync('/root/a', '/root/b');
      fs.existsSync('/root/a/index.js').should.equal(false);
      fs.statSync('/root/b/index.js').isFile().should.equal(true);
      testDone();
    });

    it('should transpose parent readdir item', function(testDone) {
      this.stubFile(this.paths[0]).readdir([
        this.stubFile(this.paths[0] + '/a')
      ]).make();

      fs.existsSync(this.paths[0] + '/a').should.equal(true);
      fs.existsSync(this.paths[0] + '/b').should.equal(false);
      fs.readdirSync(this.paths[0]).should.deep.equal(['a']);

      fs.renameSync(this.paths[0] + '/a', this.paths[0] + '/b');

      fs.existsSync(this.paths[0] + '/a').should.equal(false);
      fs.existsSync(this.paths[0] + '/b').should.equal(true);
      fs.readdirSync(this.paths[0]).should.deep.equal(['b']);
      testDone();
    });

    it('should overwrite parent readdir item', function(testDone) {
      this.stubFile(this.paths[0]).readdir([
        this.stubFile(this.paths[0] + '/a'),
        this.stubFile(this.paths[0] + '/b')
      ]).make();

      fs.existsSync(this.paths[0] + '/a').should.equal(true);
      fs.existsSync(this.paths[0] + '/b').should.equal(true);
      fs.readdirSync(this.paths[0]).should.deep.equal(['a', 'b']);

      fs.renameSync(this.paths[0] + '/a', this.paths[0] + '/b');

      fs.existsSync(this.paths[0] + '/a').should.equal(false);
      fs.existsSync(this.paths[0] + '/b').should.equal(true);
      fs.readdirSync(this.paths[0]).should.deep.equal(['b']);
      testDone();
    });

    it('should retain own readdir list', function(testDone) {
      this.stubFile(this.paths[0]).readdir([
        this.stubFile(this.paths[0] + '/a').readdir([
          this.stubFile(this.paths[0] + '/a/c')
        ])
      ]).make();

      fs.existsSync(this.paths[0] + '/a').should.equal(true);
      fs.existsSync(this.paths[0] + '/b').should.equal(false);
      fs.readdirSync(this.paths[0]).should.deep.equal(['a']);

      fs.renameSync(this.paths[0] + '/a', this.paths[0] + '/b');

      fs.existsSync(this.paths[0] + '/a').should.equal(false);
      fs.existsSync(this.paths[0] + '/b').should.equal(true);
      fs.readdirSync(this.paths[0]).should.deep.equal(['b']);

      fs.readdirSync(this.paths[0] + '/b').should.deep.equal(['c']);
      testDone();
    });

    it('should retain own readdir list after overwrite', function(testDone) {
      this.stubFile(this.paths[0]).readdir([
        this.stubFile(this.paths[0] + '/a').readdir([
          this.stubFile(this.paths[0] + '/a/c')
        ]),
        this.stubFile(this.paths[0] + '/b').readdir([
          this.stubFile(this.paths[0] + '/b/d')
        ])
      ]).make();

      fs.existsSync(this.paths[0] + '/a').should.equal(true);
      fs.existsSync(this.paths[0] + '/b').should.equal(true);
      fs.readdirSync(this.paths[0]).should.deep.equal(['a', 'b']);

      fs.renameSync(this.paths[0] + '/a', this.paths[0] + '/b');

      fs.existsSync(this.paths[0] + '/a').should.equal(false);
      fs.existsSync(this.paths[0] + '/b').should.equal(true);
      fs.readdirSync(this.paths[0]).should.deep.equal(['b']);

      fs.readdirSync(this.paths[0] + '/b').should.deep.equal(['d']);
      testDone();
    });

    it('should update parent name of children', function(testDone) {
      console.log('\x1B[33m<---------- INCOMPLETE'); testDone(); // TODO
    });

    it('should retain child meta', function(testDone) {
      // name
      // parent name (should be the parent's new name)
      // size
      console.log('\x1B[33m<---------- INCOMPLETE'); testDone(); // TODO
    });

    it('should update meta', function(testDone) {
      console.log('\x1B[33m<---------- INCOMPLETE'); testDone(); // TODO
      // name
      // parent name
      // size
    });

    it('should update after overwrite', function(testDone) {
      console.log('\x1B[33m<---------- INCOMPLETE'); testDone(); // TODO
      // name
      // parent name
      // size
    });
  });
});

<commit_msg>test(renameSync): Fix expectation<commit_after>/*jshint expr:true*/
var T = require('../..');
var fs = T.fs;
var should = T.should;
var Batch = T.Batch;

describe('fs stub', function() {
  describe('#exists', function() {
    it('should detect stub', function(testDone) {
      var self = this;
      var batch = new Batch();

      batch.push(function(taskDone) {
        fs.exists(self.paths[0], function(exists) {
          should.equal(exists, false);
          taskDone();
        });
      });

      batch.end(function() {
        self.stubFile(self.paths[0]).make();
        fs.exists(self.paths[0], function(exists) {
          should.equal(exists, true);
          testDone();
        });
      });
    });
  });

  describe('#existsSync', function() {
    it('should detect stub', function(testDone) {
      should.equal(fs.existsSync(this.paths[0]), false);
      should.equal(fs.existsSync(this.paths[1]), false);
      this.stubFile(this.paths[0]).make();
      this.stubFile(this.paths[1]).make();
      should.equal(fs.existsSync(this.paths[0]), true);
      should.equal(fs.existsSync(this.paths[1]), true);
      testDone();
    });
  });

  describe('#writeFile', function() {
    it('should update fake file map', function(testDone) {
      var self = this;
      this.stubFile(this.paths[0]).make();
      fs.writeFile(this.paths[0], this.strings[0], function(err) {
        should.equal(err, null);
        fs.readFileSync(self.paths[0]).toString().should.equal(self.strings[0]);
        testDone();
      });
    });
  });

  describe('#writeFileSync', function() {
    it('should update fake file map', function(testDone) {
      this.stubFile(this.paths[0]).make();
      fs.writeFileSync(this.paths[0], this.strings[0]);
      fs.readFileSync(this.paths[0]).toString().should.equal(this.strings[0]);
      testDone();
    });
  });

  describe('#unlink', function() {
    it('should remove itself from parent readdir', function(testDone) {
      console.log('\x1B[33m<---------- INCOMPLETE'); testDone(); // TODO
    });

    it('should update #existsSync result', function(testDone) {
      console.log('\x1B[33m<---------- INCOMPLETE'); testDone(); // TODO
    });

    it('should remove itself from stub map', function(testDone) {
      console.log('\x1B[33m<---------- INCOMPLETE'); testDone(); // TODO
    });

    it('should call unlink on child stubs', function(testDone) {
      console.log('\x1B[33m<---------- INCOMPLETE'); testDone(); // TODO
    });
  });

  describe('#renameSync', function() {
    it('should copy readFile buffers', function(testDone) {
      console.log('\x1B[33m<---------- INCOMPLETE'); testDone(); // TODO
    });

    it('should rename copied dir descendants', function(testDone) {
      this.stubFile('/root').readdir([
        this.stubFile('/root/a').readdir([
          this.stubFile('/root/a/index.js')
        ])
      ]).make();
      fs.renameSync('/root/a', '/root/b');
      fs.existsSync('/root/a/index.js').should.equal(false);
      fs.statSync('/root/b/index.js').isFile().should.equal(true);
      testDone();
    });

    it('should transpose parent readdir item', function(testDone) {
      this.stubFile(this.paths[0]).readdir([
        this.stubFile(this.paths[0] + '/a')
      ]).make();

      fs.existsSync(this.paths[0] + '/a').should.equal(true);
      fs.existsSync(this.paths[0] + '/b').should.equal(false);
      fs.readdirSync(this.paths[0]).should.deep.equal(['a']);

      fs.renameSync(this.paths[0] + '/a', this.paths[0] + '/b');

      fs.existsSync(this.paths[0] + '/a').should.equal(false);
      fs.existsSync(this.paths[0] + '/b').should.equal(true);
      fs.readdirSync(this.paths[0]).should.deep.equal(['b']);
      testDone();
    });

    it('should overwrite parent readdir item', function(testDone) {
      this.stubFile(this.paths[0]).readdir([
        this.stubFile(this.paths[0] + '/a'),
        this.stubFile(this.paths[0] + '/b')
      ]).make();

      fs.existsSync(this.paths[0] + '/a').should.equal(true);
      fs.existsSync(this.paths[0] + '/b').should.equal(true);
      fs.readdirSync(this.paths[0]).should.deep.equal(['a', 'b']);

      fs.renameSync(this.paths[0] + '/a', this.paths[0] + '/b');

      fs.existsSync(this.paths[0] + '/a').should.equal(false);
      fs.existsSync(this.paths[0] + '/b').should.equal(true);
      fs.readdirSync(this.paths[0]).should.deep.equal(['b']);
      testDone();
    });

    it('should retain own readdir list', function(testDone) {
      this.stubFile(this.paths[0]).readdir([
        this.stubFile(this.paths[0] + '/a').readdir([
          this.stubFile(this.paths[0] + '/a/c')
        ])
      ]).make();

      fs.existsSync(this.paths[0] + '/a').should.equal(true);
      fs.existsSync(this.paths[0] + '/b').should.equal(false);
      fs.readdirSync(this.paths[0]).should.deep.equal(['a']);

      fs.renameSync(this.paths[0] + '/a', this.paths[0] + '/b');

      fs.existsSync(this.paths[0] + '/a').should.equal(false);
      fs.existsSync(this.paths[0] + '/b').should.equal(true);
      fs.readdirSync(this.paths[0]).should.deep.equal(['b']);

      fs.readdirSync(this.paths[0] + '/b').should.deep.equal(['c']);
      testDone();
    });

    it('should retain own readdir list after overwrite', function(testDone) {
      this.stubFile(this.paths[0]).readdir([
        this.stubFile(this.paths[0] + '/a').readdir([
          this.stubFile(this.paths[0] + '/a/c')
        ]),
        this.stubFile(this.paths[0] + '/b').readdir([
          this.stubFile(this.paths[0] + '/b/d')
        ])
      ]).make();

      fs.existsSync(this.paths[0] + '/a').should.equal(true);
      fs.existsSync(this.paths[0] + '/b').should.equal(true);
      fs.readdirSync(this.paths[0]).should.deep.equal(['a', 'b']);

      fs.renameSync(this.paths[0] + '/a', this.paths[0] + '/b');

      fs.existsSync(this.paths[0] + '/a').should.equal(false);
      fs.existsSync(this.paths[0] + '/b').should.equal(true);
      fs.readdirSync(this.paths[0]).should.deep.equal(['b']);

      fs.readdirSync(this.paths[0] + '/b').should.deep.equal(['d', 'c']);
      testDone();
    });

    it('should update parent name of children', function(testDone) {
      console.log('\x1B[33m<---------- INCOMPLETE'); testDone(); // TODO
    });

    it('should retain child meta', function(testDone) {
      // name
      // parent name (should be the parent's new name)
      // size
      console.log('\x1B[33m<---------- INCOMPLETE'); testDone(); // TODO
    });

    it('should update meta', function(testDone) {
      console.log('\x1B[33m<---------- INCOMPLETE'); testDone(); // TODO
      // name
      // parent name
      // size
    });

    it('should update after overwrite', function(testDone) {
      console.log('\x1B[33m<---------- INCOMPLETE'); testDone(); // TODO
      // name
      // parent name
      // size
    });
  });
});

<|endoftext|>


<commit_before>// Karma configuration
// Generated on Fri Aug 29 2014 11:38:06 GMT-0400 (EDT)

module.exports = function(config) {

  config.set({

    // base path, that will be used to resolve files and exclude
    basePath: '',

    // frameworks to use
    frameworks: ['jasmine'],

    // list of files / patterns to load in the browser
    files: [
      'lib/*.js',
      '../../msl-client-browser/mockapi-browser.js',
      '../../msl-client-browser/appcontainer-driver.js',
      'spec/MSLTestSpec.js'
    ],

    // list of files to exclude
    exclude: [

    ],

    // test results reporter to use
    reporters: ['progress','junit', 'saucelabs'],

    // the default configuration
    junitReporter: {
      outputFile: 'test-results.xml',
      suite: ''
    },

    // web server port
    port: 8001,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: false,

    captureTimeout: 120000,

	browsers: ['PhantomJS'],

    // Continuous Integration mode
    // if true, it capture browsers, run tests and exit
    singleRun: true,

    proxies: {
      '/' : 'http://localhost:8002'
    },

    urlRoot: '/root',

  });
};
<commit_msg>Removed unnecessary reporter<commit_after>// Karma configuration
// Generated on Fri Aug 29 2014 11:38:06 GMT-0400 (EDT)

module.exports = function(config) {

  config.set({

    // base path, that will be used to resolve files and exclude
    basePath: '',

    // frameworks to use
    frameworks: ['jasmine'],

    // list of files / patterns to load in the browser
    files: [
      'lib/*.js',
      '../../msl-client-browser/mockapi-browser.js',
      '../../msl-client-browser/appcontainer-driver.js',
      'spec/MSLTestSpec.js'
    ],

    // list of files to exclude
    exclude: [

    ],

    // test results reporter to use
    reporters: ['progress','junit'],

    // the default configuration
    junitReporter: {
      outputFile: 'test-results.xml',
      suite: ''
    },

    // web server port
    port: 8001,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: false,

    captureTimeout: 120000,

	browsers: ['PhantomJS'],

    // Continuous Integration mode
    // if true, it capture browsers, run tests and exit
    singleRun: true,

    proxies: {
      '/' : 'http://localhost:8002'
    },

    urlRoot: '/root',

  });
};
<|endoftext|>