<commit_before>/**
 * @name jsDateFormat
 * @memberOf Date.prototype
 * @description provides functionality to control formatting with a Date object
 *
 * @author Ryan Pallas <ryan.pallas (at) gmail.com>
 * @version 2.0.0-beta.1
 * @license The MIT License (MIT)
 *
 *
 * @Copyright (c)2013-2014 Ryan Pallas <ryan.pallas (at) gmail.com>
 *
 * 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.
 *
 * Options are provided in a string, to represent the desired output
 *    NOTE: Options are not able to be repeated without separators
 *
 * Current formatting options (option are case sensitive):
 * toFormat fromFormat Option
 *   [x]       [x]     yy - 4 digit representation of the year
 *   [x]       [x]     y - 2 digit representation of the year
 *   [x]       [x]     mm - digit representation of the month (with leading zeros)
 *   [x]       [x]     m - digit representation of the month (without leading zeros)
 *   [x]       [x]     MM - full text name of the month
 *   [x]       [x]     M - short text name of the month
 *   [x]       [ ]     DD - full text name of the day
 *   [x]       [ ]     D - short text name of the day
 *   [x]       [ ]     dd - digit representation of the month (with leading zeros)
 *   [x]       [ ]     d - digit representation of the month (without leading zeros)
 *   [x]       [ ]     HH - Hours in 24 hour format (with leading zeros)
 *   [x]       [ ]     H - Hours in 24 hour format (without leading zeros)
 *   [x]       [ ]     hh - Hours in 12 hour format (with leading zeros)
 *   [x]       [ ]     h - hours in 12 hour format (without leading zeros)
 *   [x]       [ ]     nn - Minutes (with leading zeros)
 *   [x]       [ ]     n - Minutes (without leading zeros)
 *   [x]       [ ]     ss - Seconds (with leading zeros)
 *   [x]       [ ]     s - Seconds (without leading zeros)
 *   [x]       [ ]     j - 12 hour section lowercase (am/pm)
 *   [x]       [ ]     J - 12 hour seciton uppercase (AM/PM)
 *   [x]       [ ]     z - Timezone as offset from UTC
 *
 * ToDo: support the following options:
 *    - More Localizations
 *    - More options
 */
Date.prototype.jsDateFormat = {
    that: this,
    localizations: {
        'default': 'en',
        supported: ['en','es'],
        Months: {
            'en' : ['January', 'February', 'March', 'April', 'May',
                'June', 'July', 'August', 'September', 'October',
                'November', 'December'],
            'es' : ['enero', 'febrero', 'marzo', 'abril', 'mayo',
                'junio', 'julio', 'agosto', 'septiembre',
                'octubre', 'noviembre', 'diciembre']
        },
        MonthAbbr: {
            'en' : ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June',
                'July', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
            'es' : ['enero', 'feb', 'marzo', 'abr', 'mayo',
                'jun', 'jul', 'agosto', 'set', 'oct', 'nov', 'dic']
        },
        Days: {
            'en' : ['Sunday', 'Monday', 'Tuesday', 'Wednesday',
                'Thursday', 'Friday', 'Saturday'],
            'es' : ['lunes','martes','mircoles','jueves',
                'viernes','sbado','domingo']
        },
        DayAbbr: {
            'en' : ['Su', 'M', 'Tu', 'W', 'Th', 'F', 'Sa'],
            'es' : ['l', 'ma', 'mi', 'j', 'v', 's', 'd']
        }
    },
    setLocalization: function(strLocalization) {
        if (that.localizations.supported.indexOf(strLocalization) > -1) {
            that.localization = strLocalization;
        } else if(that.localizations.supported.indexOf(that.localization) == -1) {
            that.localization = that.localizations.default;
        }
    },
    getTZ: function(iTZMin) {
        var bBehindUTC = true;
        if( iTZMin <= 0 ) {
            iTZMin = Math.abs(iTZMin);
            bBehindUTC = false;
        }
        var iTZHr = iTZMin / 60;
        iTZMin = iTZMin % 60;
        return (bBehindUTC ? '-' : '+') +
            (iTZHr < 10 ? '0' : '') + iTZHr +
            (iTZMin < 10 ? '0' : '') + iTZMin;
    },
    getFunc: function(str, type) {
        var strRetVal = type == 'read' ? 'get' :
                        type == 'write' ? 'set' : '';
        switch(str) {
            case 'y':
                strRetVal += 'FullYear';
                break;
            case 'm':
            case 'M':
                strRetVal += 'Month';
                break;
            case 'd':
                strRetVal += 'Date';
                break;
            case 'D':
                if (type == 'read') {
                    strRetVal += 'Day';
                } else {
                    strRetVal = '';
                }
                break;
            case 'H':
            case 'h':
            case 'j':
            case 'J':
                strRetVal += 'Hours';
                break;
            case 'n':
                strRetVal += 'Minutes';
                break;
            case 's':
                strRetVal += 'Seconds';
                break;
            default:
                strRetVal = '';
        }
        return strRetVal;
    }
};

/**
 * @name format
 * @memberOf Date.prototype
 * @deprecated since v2.0.0 - use toFormat instead
 * @param string strFormat The format of the returned date string
 * @returns string The formatted date string
 */
Date.prototype.format = function (strFormat) {
    return this.toFormat(strFormat);
};

/**
 * @name toFormat
 * @memberOf Date.prototype
 * @param string strFormat The format of the returned date string
 * @returns string The formatted date string
 */
Date.prototype.toFormat = function(strFormat) {
        // init jsDateFormat
    var jsd = this.jsDateFormat,
        strLoc = jsd.localization || jsd.localizations['default'];
    var strRetVal = '',
        chPrev = '',
        iCharCount = 0,
        i,
        mVal,
        f;
    for ( i = 0; i <= strFormat.length; i++) {
        if (strFormat.charAt(i) == chPrev) {
            iCharCount++;
        }
        else {
            if( chPrev != '' ) {
                f = jsd.getFunc(chPrev, 'read');
                if ( typeof this[f] == 'function') {
                    mVal = this[f]();
                }
                if ( chPrev == 'h' ) {
                    mVal = (mVal + 11) % 12 + 1;
                }
                if (
                    (iCharCount == 2 && chPrev == 'y') ||
                    (iCharCount == 1 && 'dHnsh'.indexOf(chPrev) > -1)
                ) {
                    strRetVal += mVal;
                } else if ( iCharCount == 2 && 'mdHnsh'.indexOf(chPrev) > -1 ) {
                    mVal += chPrev == 'm' ? 1 : 0;
                    strRetVal += (mVal < 10 ? '0' : '') + mVal;
                } else {
                    switch(chPrev) {
                        case 'y':
                            if (iCharCount == 1) {
                                strRetVal += mVal.toString().substring(2,4);
                                break;
                            }
                        case 'm':
                            if (iCharCount == 1) {
                                mVal++;
                                strRetVal += mVal;
                                break;
                            }
                        case 'M':
                            if (iCharCount == 2) {
                                strRetVal += this.jsDateFormat.localizations.
                                    Months[strLoc][mVal];
                                break;
                            }
                            else if (iCharCount == 1) {
                                strRetVal += this.jsDateFormat.localizations.
                                    MonthAbbr[strLoc][mVal];
                                break;
                            }
                        case 'D':
                            if (iCharCount == 2) {
                                strRetVal += this.jsDateFormat.localizations.
                                    Days[strLoc][mVal];
                                break;
                            }
                            else if (iCharCount == 1) {
                                strRetVal += this.jsDateFormat.localizations.
                                    DayAbbr[strLoc][mVal];
                                break;
                            }
                        case 'j':
                            if (iCharCount == 1) {
                                strRetVal += mVal > 0 && mVal < 13 ? 'am' : 'pm';
                                break;
                            }
                        case 'J':
                            if (iCharCount == 1) {
                                strRetVal += mVal > 0 && mVal < 13 ? 'AM' : 'PM';
                                break;
                            }
                        case 'z':
                            if (iCharCount == 1) {
                                strRetVal += jsd.getTZ(this.getTimezoneOffset());
                                break;
                            }
                        default:
                            strRetVal += new Array(iCharCount + 1).join(chPrev);
                    }
                }
            }
            chPrev = strFormat.charAt(i);
            iCharCount = 1;
        }
    }

    return strRetVal;
};

/**
 * @name fromFormat
 * @memberOf Date
 * @param string strValue A string representing a Date and/or time
 * @param string strFormat The format of the date string
 * @returns Date The date object, whose values are set to what was passed in
 */
Date.fromFormat = function(strValue, strFormat) {
    var oDate = new Date(),
        jsd = oDate.jsDateFormat,
        strLoc = jsd.localization || jsd.localizations['default'],
        chPrev = '',
        iCharCount = 0,
        i,
        mVal,
        f;
    for ( i = 0; i <= strFormat.length; i++) {
        if (strFormat.charAt(i) == chPrev) {
            iCharCount++;
        }
        else {
            f = jsd.getFunc(chPrev, 'write');
            if( chPrev != '' && typeof oDate[f] == 'function' ) {
                switch (chPrev) {
                    case 'y':
                        if( iCharCount == 2 ) {
                            mVal = strValue.substr(0,4);
                            strValue = strValue.substr(4);
                            oDate[f](mVal);
                            break;
                        } else if ( iCharCount == 1 ) {
                            mVal = strValue.substr(0,2);
                            strValue = strValue.substr(2);
                            if( mVal < 30 )
                                mVal = '20' + mVal;
                            else
                                mVal = '19' + mVal;
                            oDate[f](mVal);
                            break;
                        }
                    case 'm':
                        if ( iCharCount == 2 ) {
                            mVal = parseInt(strValue.substr(0,2), 10) - 1;
                            strValue = strValue.substr(2);
                            oDate[f](mVal);
                            break;
                        } else if ( iCharCount == 1 ) {
                            mVal = parseInt(strValue.substr(0,2), 10);
                            if ( mVal == 10 || mVal == 11 || mVal == 12 ) {
                                strValue = strValue.substr(2);
                            } else {
                                mVal = parseInt(strValue.substr(0,1), 10) - 1;
                                strValue = strValue.substr(1);
                            }
                            oDate[f](mVal);
                            break;
                        }
                    case 'M':
                        if ( iCharCount == 2 || iCharCount == 1 ) {
                            var re, mo;
                            mVal = '';
                            for( var i = 0; i < 12; i++ ) {
                                if ( iCharCount == 2 ) {
                                    mo = jsd.localizations.Months[strLoc][i];
                                } else {
                                    mo = jsd.localizations.MonthAbbr[strLoc][i];
                                }
                                re = new RegExp("^" + mo);
                                if ( strValue.match(re)) {
                                    mVal = i;
                                    strValue = strValue.substr(mo.length);
                                    break;
                                }
                            }
                            if ( mVal != '' ) {
                                oDate[f](mVal);
                                break;
                            }
                        }
                    case 'd':
                        if ( iCharCount == 2 ) {
                            mVal = parseInt(strValue.substr(0,2), 10);
                            strValue = strValue.substr(2);
                            oDate[f](mVal);
                            break;
                        } else if ( iCharCount == 1 ) {
                            mVal = strValue.match(/^3[0-1]|[1-2][0-9]|[0-9]/);
                            if ( mVal !== null ) {
                                strVal = strValue.substr(mVal.length);
                                oDate[f](mVal);
                                break;
                            }
                        }
                    case 'H':
                        if ( iCharCount == 2 ) {
                            mVal = parseInt(strValue.substr(0,2), 10);
                            strValue = strValue.substr(2);
                            oDate[f](mVal);
                            break;
                        } else if ( iCharCount == 1 ) {
                            mVal = strValue.match(/^2[0-3]|1[0-9]|[0-9]/);
                            if ( mVal !== null ) {
                                strValue = strValue.substr(mVal.length);
                                oDate[f](mVal);
                                break;
                            }
                        }
                    case 'h':
                        if ( iCharCount == 2 ) {
                            mVal = parseInt(strValue.substr(0,2), 10);
                            strValue = strValue.substr(2);
                            oDate[f](mVal);
                            break;
                        } else if ( iCharCount == 1 ) {
                            mVal = strValue.match(/^1[0-2]|[1-9]/);
                            if ( mVal !== null ) {
                                strValue = strValue.substr(mVal.length);
                                oDate[f](mVal);
                                break;
                            }
                        }
                    case 'n':
                        if ( iCharCount == 2 ) {
                            mVal = parseInt(strValue.substr(0,2), 10);
                            strValue = strValue.substr(2);
                            oDate[f](mVal);
                            break;
                        } else if ( iCharCount == 1 ) {
                            mVal = strValue.match(/^[1-5][0-9]|[0-9]/);
                            if ( mVal !== null ) {
                                strValue = strValue.substr(mVal.length);
                                oDate[f](mVal);
                                break;
                            }
                        }
                    case 's':
                        if ( iCharCount == 2 ) {
                            mVal = parseInt(strValue.substr(0,2), 10);
                            strValue = strValue.substr(2);
                            oDate[f](mVal);
                            break;
                        } else if ( iCharCount == 1 ) {
                            mVal = strValue.match(/^[1-5][0-9]|[0-9]/);
                            if ( mVal !== null ) {
                                strValue = strValue.substr(mVal.length);
                                oDate[f](mVal);
                                break;
                            }
                        }
                }
            }
            chPrev = strFormat.charAt(i);
            iCharCount = 1;
        }
    }
    return oDate;
};

<commit_msg>am/pm support + defaults for fromFormat<commit_after>/**
 * @name jsDateFormat
 * @memberOf Date.prototype
 * @description provides functionality to control formatting with a Date object
 *
 * @author Ryan Pallas <ryan.pallas (at) gmail.com>
 * @version 2.0.0-beta.1
 * @license The MIT License (MIT)
 *
 *
 * @Copyright (c)2013-2014 Ryan Pallas <ryan.pallas (at) gmail.com>
 *
 * 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.
 *
 * Options are provided in a string, to represent the desired output
 *    NOTE: Options are not able to be repeated without separators
 *
 * Current formatting options (option are case sensitive):
 * toFormat fromFormat Option
 *   [x]       [x]     yy - 4 digit representation of the year
 *   [x]       [x]     y - 2 digit representation of the year
 *   [x]       [x]     mm - digit representation of the month (with leading zeros)
 *   [x]       [x]     m - digit representation of the month (without leading zeros)
 *   [x]       [x]     MM - full text name of the month
 *   [x]       [x]     M - short text name of the month
 *   [x]       [ ]     DD - full text name of the day
 *   [x]       [ ]     D - short text name of the day
 *   [x]       [ ]     dd - digit representation of the month (with leading zeros)
 *   [x]       [ ]     d - digit representation of the month (without leading zeros)
 *   [x]       [ ]     HH - Hours in 24 hour format (with leading zeros)
 *   [x]       [ ]     H - Hours in 24 hour format (without leading zeros)
 *   [x]       [ ]     hh - Hours in 12 hour format (with leading zeros)
 *   [x]       [ ]     h - hours in 12 hour format (without leading zeros)
 *   [x]       [ ]     nn - Minutes (with leading zeros)
 *   [x]       [ ]     n - Minutes (without leading zeros)
 *   [x]       [ ]     ss - Seconds (with leading zeros)
 *   [x]       [ ]     s - Seconds (without leading zeros)
 *   [x]       [ ]     j - 12 hour section lowercase (am/pm)
 *   [x]       [ ]     J - 12 hour seciton uppercase (AM/PM)
 *   [x]       [ ]     z - Timezone as offset from UTC
 *
 * ToDo: support the following options:
 *    - More Localizations
 *    - More options
 */
Date.prototype.jsDateFormat = {
    that: this,
    localizations: {
        'default': 'en',
        supported: ['en','es'],
        Months: {
            'en' : ['January', 'February', 'March', 'April', 'May',
                'June', 'July', 'August', 'September', 'October',
                'November', 'December'],
            'es' : ['enero', 'febrero', 'marzo', 'abril', 'mayo',
                'junio', 'julio', 'agosto', 'septiembre',
                'octubre', 'noviembre', 'diciembre']
        },
        MonthAbbr: {
            'en' : ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June',
                'July', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
            'es' : ['enero', 'feb', 'marzo', 'abr', 'mayo',
                'jun', 'jul', 'agosto', 'set', 'oct', 'nov', 'dic']
        },
        Days: {
            'en' : ['Sunday', 'Monday', 'Tuesday', 'Wednesday',
                'Thursday', 'Friday', 'Saturday'],
            'es' : ['lunes','martes','mircoles','jueves',
                'viernes','sbado','domingo']
        },
        DayAbbr: {
            'en' : ['Su', 'M', 'Tu', 'W', 'Th', 'F', 'Sa'],
            'es' : ['l', 'ma', 'mi', 'j', 'v', 's', 'd']
        }
    },
    setLocalization: function(strLocalization) {
        if (that.localizations.supported.indexOf(strLocalization) > -1) {
            that.localization = strLocalization;
        } else if(that.localizations.supported.indexOf(that.localization) == -1) {
            that.localization = that.localizations.default;
        }
    },
    getTZ: function(iTZMin) {
        var bBehindUTC = true;
        if( iTZMin <= 0 ) {
            iTZMin = Math.abs(iTZMin);
            bBehindUTC = false;
        }
        var iTZHr = iTZMin / 60;
        iTZMin = iTZMin % 60;
        return (bBehindUTC ? '-' : '+') +
            (iTZHr < 10 ? '0' : '') + iTZHr +
            (iTZMin < 10 ? '0' : '') + iTZMin;
    },
    getFunc: function(str, type) {
        var strRetVal = type == 'read' ? 'get' :
                        type == 'write' ? 'set' : '';
        switch(str) {
            case 'y':
                strRetVal += 'FullYear';
                break;
            case 'm':
            case 'M':
                strRetVal += 'Month';
                break;
            case 'd':
                strRetVal += 'Date';
                break;
            case 'D':
                if (type == 'read') {
                    strRetVal += 'Day';
                } else {
                    strRetVal = '';
                }
                break;
            case 'H':
            case 'h':
            case 'j':
            case 'J':
                strRetVal += 'Hours';
                break;
            case 'n':
                strRetVal += 'Minutes';
                break;
            case 's':
                strRetVal += 'Seconds';
                break;
            default:
                strRetVal = '';
        }
        return strRetVal;
    }
};

/**
 * @name format
 * @memberOf Date.prototype
 * @deprecated since v2.0.0 - use toFormat instead
 * @param string strFormat The format of the returned date string
 * @returns string The formatted date string
 */
Date.prototype.format = function (strFormat) {
    return this.toFormat(strFormat);
};

/**
 * @name toFormat
 * @memberOf Date.prototype
 * @param string strFormat The format of the returned date string
 * @returns string The formatted date string
 */
Date.prototype.toFormat = function(strFormat) {
        // init jsDateFormat
    var jsd = this.jsDateFormat,
        strLoc = jsd.localization || jsd.localizations['default'];
    var strRetVal = '',
        chPrev = '',
        iCharCount = 0,
        i,
        mVal,
        f;
    for ( i = 0; i <= strFormat.length; i++) {
        if (strFormat.charAt(i) == chPrev) {
            iCharCount++;
        }
        else {
            if( chPrev != '' ) {
                f = jsd.getFunc(chPrev, 'read');
                if ( typeof this[f] == 'function') {
                    mVal = this[f]();
                }
                if ( chPrev == 'h' ) {
                    mVal = (mVal + 11) % 12 + 1;
                }
                if (
                    (iCharCount == 2 && chPrev == 'y') ||
                    (iCharCount == 1 && 'dHnsh'.indexOf(chPrev) > -1)
                ) {
                    strRetVal += mVal;
                } else if ( iCharCount == 2 && 'mdHnsh'.indexOf(chPrev) > -1 ) {
                    mVal += chPrev == 'm' ? 1 : 0;
                    strRetVal += (mVal < 10 ? '0' : '') + mVal;
                } else {
                    switch(chPrev) {
                        case 'y':
                            if (iCharCount == 1) {
                                strRetVal += mVal.toString().substring(2,4);
                                break;
                            }
                        case 'm':
                            if (iCharCount == 1) {
                                mVal++;
                                strRetVal += mVal;
                                break;
                            }
                        case 'M':
                            if (iCharCount == 2) {
                                strRetVal += this.jsDateFormat.localizations.
                                    Months[strLoc][mVal];
                                break;
                            }
                            else if (iCharCount == 1) {
                                strRetVal += this.jsDateFormat.localizations.
                                    MonthAbbr[strLoc][mVal];
                                break;
                            }
                        case 'D':
                            if (iCharCount == 2) {
                                strRetVal += this.jsDateFormat.localizations.
                                    Days[strLoc][mVal];
                                break;
                            }
                            else if (iCharCount == 1) {
                                strRetVal += this.jsDateFormat.localizations.
                                    DayAbbr[strLoc][mVal];
                                break;
                            }
                        case 'j':
                            if (iCharCount == 1) {
                                strRetVal += mVal > 0 && mVal < 13 ? 'am' : 'pm';
                                break;
                            }
                        case 'J':
                            if (iCharCount == 1) {
                                strRetVal += mVal > 0 && mVal < 13 ? 'AM' : 'PM';
                                break;
                            }
                        case 'z':
                            if (iCharCount == 1) {
                                strRetVal += jsd.getTZ(this.getTimezoneOffset());
                                break;
                            }
                        default:
                            strRetVal += new Array(iCharCount + 1).join(chPrev);
                    }
                }
            }
            chPrev = strFormat.charAt(i);
            iCharCount = 1;
        }
    }

    return strRetVal;
};

/**
 * @name fromFormat
 * @memberOf Date
 * @param string strValue A string representing a Date and/or time
 * @param string strFormat The format of the date string
 * @returns Date The date object, whose values are set to what was passed in
 */
Date.fromFormat = function(strValue, strFormat) {
    var oDate = new Date(),
        jsd = oDate.jsDateFormat,
        strLoc = jsd.localization || jsd.localizations['default'],
        chPrev = '',
        iCharCount = 0,
        i,
        mVal,
        f;
    for ( i = 0; i <= strFormat.length; i++) {
        if (strFormat.charAt(i) == chPrev) {
            iCharCount++;
        }
        else {
            f = jsd.getFunc(chPrev, 'write');
            if( chPrev != '' && typeof oDate[f] == 'function' ) {
                switch (chPrev) {
                    case 'y':
                        if( iCharCount == 2 ) {
                            mVal = strValue.substr(0,4);
                            strValue = strValue.substr(4);
                            oDate[f](mVal);
                            break;
                        } else if ( iCharCount == 1 ) {
                            mVal = strValue.substr(0,2);
                            strValue = strValue.substr(2);
                            if( mVal < 30 )
                                mVal = '20' + mVal;
                            else
                                mVal = '19' + mVal;
                            oDate[f](mVal);
                            break;
                        }
                    case 'm':
                        if ( iCharCount == 2 ) {
                            mVal = parseInt(strValue.substr(0,2), 10) - 1;
                            strValue = strValue.substr(2);
                            oDate[f](mVal);
                            break;
                        } else if ( iCharCount == 1 ) {
                            mVal = parseInt(strValue.substr(0,2), 10);
                            if ( mVal == 10 || mVal == 11 || mVal == 12 ) {
                                strValue = strValue.substr(2);
                            } else {
                                mVal = parseInt(strValue.substr(0,1), 10) - 1;
                                strValue = strValue.substr(1);
                            }
                            oDate[f](mVal);
                            break;
                        }
                    case 'M':
                        if ( iCharCount == 2 || iCharCount == 1 ) {
                            var re, mo;
                            mVal = '';
                            for( var i = 0; i < 12; i++ ) {
                                if ( iCharCount == 2 ) {
                                    mo = jsd.localizations.Months[strLoc][i];
                                } else {
                                    mo = jsd.localizations.MonthAbbr[strLoc][i];
                                }
                                re = new RegExp("^" + mo);
                                if ( strValue.match(re)) {
                                    mVal = i;
                                    strValue = strValue.substr(mo.length);
                                    break;
                                }
                            }
                            if ( mVal != '' ) {
                                oDate[f](mVal);
                                break;
                            }
                        }
                    case 'd':
                        if ( iCharCount == 2 ) {
                            mVal = parseInt(strValue.substr(0,2), 10);
                            strValue = strValue.substr(2);
                            oDate[f](mVal);
                            break;
                        } else if ( iCharCount == 1 ) {
                            mVal = strValue.match(/^3[0-1]|[1-2][0-9]|[0-9]/);
                            if ( mVal !== null ) {
                                strVal = strValue.substr(mVal.length);
                                oDate[f](mVal);
                                break;
                            }
                        }
                    case 'H':
                        if ( iCharCount == 2 ) {
                            mVal = parseInt(strValue.substr(0,2), 10);
                            strValue = strValue.substr(2);
                            oDate[f](mVal);
                            break;
                        } else if ( iCharCount == 1 ) {
                            mVal = strValue.match(/^2[0-3]|1[0-9]|[0-9]/);
                            if ( mVal !== null ) {
                                strValue = strValue.substr(mVal.length);
                                oDate[f](mVal);
                                break;
                            }
                        }
                    case 'h':
                        if ( iCharCount == 2 ) {
                            mVal = parseInt(strValue.substr(0,2), 10);
                            strValue = strValue.substr(2);
                            oDate[f](mVal);
                            break;
                        } else if ( iCharCount == 1 ) {
                            mVal = strValue.match(/^1[0-2]|[1-9]/);
                            if ( mVal !== null ) {
                                strValue = strValue.substr(mVal.length);
                                oDate[f](mVal);
                                break;
                            }
                        }
                    case 'n':
                        if ( iCharCount == 2 ) {
                            mVal = parseInt(strValue.substr(0,2), 10);
                            strValue = strValue.substr(2);
                            oDate[f](mVal);
                            break;
                        } else if ( iCharCount == 1 ) {
                            mVal = strValue.match(/^[1-5][0-9]|[0-9]/);
                            if ( mVal !== null ) {
                                strValue = strValue.substr(mVal.length);
                                oDate[f](mVal);
                                break;
                            }
                        }
                    case 's':
                        if ( iCharCount == 2 ) {
                            mVal = parseInt(strValue.substr(0,2), 10);
                            strValue = strValue.substr(2);
                            oDate[f](mVal);
                            break;
                        } else if ( iCharCount == 1 ) {
                            mVal = strValue.match(/^[1-5][0-9]|[0-9]/);
                            if ( mVal !== null ) {
                                strValue = strValue.substr(mVal.length);
                                oDate[f](mVal);
                                break;
                            }
                        }
                    case 'J':
                    case 'j':
                        mVal = strValue.substr(0,2).toLowerCase();
                        strValue = strValue.substr(2);
                        if ( mVal == 'pm' && oDate.getHours() < 12 ) {
                            oDate.setHours(oDate.getHours() + 12);
                            break;
                        } else if ( mVal == 'am' && oDate.getHours() > 11 ) {
                            oDate.setHours(oDate.getHours() - 12);
                            break;
                        }
                    default:
                        strValue = strValue.substr(iCharCount);
                }
            } else {
                strValue = strValue.substr(iCharCount);
            }
            chPrev = strFormat.charAt(i);
            iCharCount = 1;
        }
    }
    return oDate;
};

<|endoftext|>


<commit_before>const React = require('react');
const PropTypes = require('prop-types');
const DartHelpers = require('./_dart_helpers');
const CreateReactClass = require('create-react-class');

window.React = React;
Object.assign(window, DartHelpers);

React.createClass = CreateReactClass; // TODO: Remove this once over_react_test doesnt rely on createClass.
React.PropTypes = PropTypes; // Only needed to support legacy context until we update.

if (process.env.NODE_ENV == 'production') {
    require('./dart_env_prod');
} else {
    require('./dart_env_dev');
}

require('./polyfill/delete_property');
require('./polyfill/object_assign');
<commit_msg>Move polyfills to top<commit_after>require('./polyfill/delete_property');
require('./polyfill/object_assign');

const React = require('react');
const PropTypes = require('prop-types');
const DartHelpers = require('./_dart_helpers');
const CreateReactClass = require('create-react-class');

window.React = React;
Object.assign(window, DartHelpers);

React.createClass = CreateReactClass; // TODO: Remove this once over_react_test doesnt rely on createClass.
React.PropTypes = PropTypes; // Only needed to support legacy context until we update.

if (process.env.NODE_ENV == 'production') {
    require('./dart_env_prod');
} else {
    require('./dart_env_dev');
}
<|endoftext|>


<commit_before>import {
  BRICK,
  MAIN_BRICK
} from './componentNames'

export const ERROR = 'error'

export const doesAllInputsHaveValues = (element, valueIds, unitTest) => {
  const numberOfInputs = Object.keys(element.inputSlots).length

  return (numberOfInputs == valueIds.length) &&
    (unitTestValues(valueIds, unitTest).length == numberOfInputs)
}

export const elementInputValueIds = (element) => {
  let valueIds = []

  for(let id in element.inputSlots) {
    const inputSlot = element.inputSlots[id]

    if(inputSlot.value) {
      valueIds[inputSlot.index] = inputSlot.value.slotId
    }
  }

  return valueIds
}

const unitTestValues = (valueIds, unitTest) => {
  let values = []

  for(let id in valueIds) {
    const valueId = valueIds[id]
    const element = unitTest.values[valueId]

    if(element && element.type !== ERROR && element.value) {
      values.push({
        id: valueId,
        type: element.type,
        value: element.value
      })
    }
  }

  return values
}

const nativeBricks = {
  arithmetic: {
    "+": (a, b) => a + b,
    "-": (a, b) => a - b,
    "*": (a, b) => a * b,
    "/": (a, b) => a / b
  },
  boolean: {
    "&&": (a, b) => a && b,
    "||": (a, b) => a || b,
    "!": (a) => !a
  },
  eq: {
    "==": (a, b) => a === b,
  },
  hof: {
    "join": (a) => a.join(''),
    "sort": (a) => a.slice(0).sort()
  },
  string: {
    "concat": (a, b) => a.concat(b),
    "length": (a) => a.length,
    "reverse": (a) => a.split('').reverse().join(''),
    "split": (a) => a.split(''),
  }
}

const parsers = {
  boolean: (value) => value.toLowerCase() == "true",
  list: (value) => value.split(','),
  number: (value) => parseFloat(value),
  string: (value) => value
}

const _evalBrick = (brick, args) => {
  const { moduleName, name } = brick
  const brickOutput = nativeBricks[moduleName][name].apply(null, args)

  if(brickOutput === undefined) {
    return {
      componentName: BRICK,
      type: ERROR,
      value: 'error'
    }
  }

  return {
    componentName: BRICK,
    type: _getOutputType(brickOutput),
    value: brickOutput.toString()
  }
}

export const tryEvalPath = (workspace, unitTest, elementId) => {
  let newUnitTest = Object.assign({}, unitTest)

  return _tryEvalPath(workspace, newUnitTest, elementId)
}

const _tryEvalPath = (workspace, unitTest, elementId) => {
  const brick = workspace.entities[elementId]
  let args = []

  if(brick.componentName != BRICK) {
    return unitTest
  }

  for(var id in brick.inputSlots) {
    const { value } = brick.inputSlots[id]
    const slotValue = unitTest.values[value.slotId]

    if(slotValue && slotValue.type && slotValue.value) {
      args.push(parsers[slotValue.type](slotValue.value))
    } else {
      return unitTest
    }
  }

  const outputSlotId = Object.keys(brick.outputSlots)[0]
  const outputSlot = brick.outputSlots[outputSlotId]

  unitTest.values[outputSlotId] = _evalBrick(brick, args)

  outputSlot.outputElementIds.forEach((id) =>
    _tryEvalPath(workspace, unitTest, id)
  )

  return unitTest
}

export const evalPathValueIds = (workspace, element, valueIds) => {
  if(element.componentName != MAIN_BRICK) {
    const outputSlotId = Object.keys(element.outputSlots)[0]
    const outputSlot = element.outputSlots[outputSlotId]

    valueIds.push(outputSlot.id)

    if(outputSlot.outputElementIds) {
      outputSlot.outputElementIds.forEach((id) => {
        evalPathValueIds(workspace, workspace.entities[id], valueIds)
      })
    }
  }

  return valueIds
}

const _getOutputType = (output) => {
  if(Array.isArray(output)) {
    return 'list'
  } else {
    return typeof output
  }
}
<commit_msg>Wrap brick evaluation inside try/catch<commit_after>import {
  BRICK,
  MAIN_BRICK
} from './componentNames'

export const ERROR = 'error'

export const doesAllInputsHaveValues = (element, valueIds, unitTest) => {
  const numberOfInputs = Object.keys(element.inputSlots).length

  return (numberOfInputs == valueIds.length) &&
    (unitTestValues(valueIds, unitTest).length == numberOfInputs)
}

export const elementInputValueIds = (element) => {
  let valueIds = []

  for(let id in element.inputSlots) {
    const inputSlot = element.inputSlots[id]

    if(inputSlot.value) {
      valueIds[inputSlot.index] = inputSlot.value.slotId
    }
  }

  return valueIds
}

const unitTestValues = (valueIds, unitTest) => {
  let values = []

  for(let id in valueIds) {
    const valueId = valueIds[id]
    const element = unitTest.values[valueId]

    if(element && element.type !== ERROR && element.value) {
      values.push({
        id: valueId,
        type: element.type,
        value: element.value
      })
    }
  }

  return values
}

const nativeBricks = {
  arithmetic: {
    "+": (a, b) => a + b,
    "-": (a, b) => a - b,
    "*": (a, b) => a * b,
    "/": (a, b) => a / b
  },
  boolean: {
    "&&": (a, b) => a && b,
    "||": (a, b) => a || b,
    "!": (a) => !a
  },
  eq: {
    "==": (a, b) => a === b,
  },
  hof: {
    "join": (a) => a.join(''),
    "sort": (a) => a.slice(0).sort()
  },
  string: {
    "concat": (a, b) => a.concat(b),
    "length": (a) => a.length,
    "reverse": (a) => a.split('').reverse().join(''),
    "split": (a) => a.split(''),
  }
}

const parsers = {
  boolean: (value) => value.toLowerCase() == "true",
  list: (value) => value.split(','),
  number: (value) => parseFloat(value),
  string: (value) => value
}

const _evalBrick = (brick, args) => {
  const { moduleName, name } = brick
  let brickOutput

  try {
    brickOutput = nativeBricks[moduleName][name].apply(null, args)
  } catch(error) {
  }

  if(brickOutput === undefined) {
    return {
      componentName: BRICK,
      type: ERROR,
      value: 'error'
    }
  }

  return {
    componentName: BRICK,
    type: _getOutputType(brickOutput),
    value: brickOutput.toString()
  }
}

export const tryEvalPath = (workspace, unitTest, elementId) => {
  let newUnitTest = Object.assign({}, unitTest)

  return _tryEvalPath(workspace, newUnitTest, elementId)
}

const _tryEvalPath = (workspace, unitTest, elementId) => {
  const brick = workspace.entities[elementId]
  let args = []

  if(brick.componentName != BRICK) {
    return unitTest
  }

  for(var id in brick.inputSlots) {
    const { value } = brick.inputSlots[id]
    const slotValue = unitTest.values[value.slotId]

    if(slotValue && slotValue.type && slotValue.value) {
      args.push(parsers[slotValue.type](slotValue.value))
    } else {
      return unitTest
    }
  }

  const outputSlotId = Object.keys(brick.outputSlots)[0]
  const outputSlot = brick.outputSlots[outputSlotId]

  unitTest.values[outputSlotId] = _evalBrick(brick, args)

  outputSlot.outputElementIds.forEach((id) =>
    _tryEvalPath(workspace, unitTest, id)
  )

  return unitTest
}

export const evalPathValueIds = (workspace, element, valueIds) => {
  if(element.componentName != MAIN_BRICK) {
    const outputSlotId = Object.keys(element.outputSlots)[0]
    const outputSlot = element.outputSlots[outputSlotId]

    valueIds.push(outputSlot.id)

    if(outputSlot.outputElementIds) {
      outputSlot.outputElementIds.forEach((id) => {
        evalPathValueIds(workspace, workspace.entities[id], valueIds)
      })
    }
  }

  return valueIds
}

const _getOutputType = (output) => {
  if(Array.isArray(output)) {
    return 'list'
  } else {
    return typeof output
  }
}
<|endoftext|>


<commit_before>#!/usr/bin/env node

const fs = require('fs');
const path = require('path');
const execSync = require('child_process').execSync;
const chalk = require('chalk');
const readline = require('readline');
const semver = require('semver');
const autoDistTag = require('auto-dist-tag');
const Project = require('../build/utils/project');

const DIST_PATH = path.resolve(__dirname, '../dist');
const PACKAGES_PATH = path.resolve(__dirname, '../packages');

const DRY_RUN = process.argv.indexOf('--dry-run') > -1;
if (DRY_RUN) {
  console.log(chalk.yellow("--dry-run"), "- side effects disabled");
}

// Fail fast if we haven't done a build first.
assertDistExists();
assertGitIsClean();
assertPassesSmokeTest();

let cli = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

// Load up the built packages in dist.
let packages = Project.from(DIST_PATH)
  .packages
  .filter(pkg => pkg.isPublishable);

let packageNames = packages.map(pkg => pkg.name);
let newVersion;

// Begin interactive CLI
printExistingVersions();
promptForVersion()
  .finally(() => cli.close())
  .catch(reason => {
    console.error(reason);
    process.exit(1);
  });

function question(prompt) {
  return new Promise(resolve => {
    cli.question(prompt, resolve);
  });
}

function printExistingVersions() {
  let packageVersions = packages.map(pkg => [pkg.name, pkg.version]);
  printPadded(packageVersions);
}

async function promptForVersion() {
  let defaultVersion = generateDefaultVersion();

  let version = await question(chalk.green(`\nNew version to publish? [${defaultVersion}] `));
  version = version.trim();
  if (version === '') {
    version = defaultVersion;
  }

  await validateNewVersion(version);
  console.log(chalk.green(`Publishing v${version}...`));

  newVersion = version;
  await applyNewVersion();
  await gitCommitAndTag();
  await confirmPublish();
}

function generateDefaultVersion() {
  let currentVersion = require('../package.json').version;
  return semver.inc(currentVersion, 'patch');
}

function validateNewVersion(version) {
  if (version === '') { fatalError("Version must not be empty."); }
  if (!semver.valid(version)) { fatalError("Version must be a valid SemVer version."); }

  packages.forEach(pkg => {
    if (!semver.gt(version, pkg.version)) {
      fatalError(`Version must be greater than existing versions. ${pkg.name} has version ${pkg.version}, which is greater than or equal to ${version}.`);
    }
  });
}

function applyNewVersion() {
  console.log(`Apply ${newVersion}`);

  // Update root package.json
  let rootPkgPath = path.join(__dirname, '../package.json');
  let rootPkg = JSON.parse(fs.readFileSync(rootPkgPath, 'utf8'));
  let rootSimpleHTMLTokenizerVersion = rootPkg.dependencies['simple-html-tokenizer'];

  // Update packages in the dist directory
  packages.forEach(pkg => {
    pkg.pkg.version = newVersion;
    pkg.updateDependencies(newVersion);
    pkg.updateSimpleHTMLTokenizer(rootSimpleHTMLTokenizerVersion);

    if (!DRY_RUN) {
      pkg.savePackageJSON();
    }
  });

  // Update source packages
  Project.from(PACKAGES_PATH)
    .packages
    .forEach(pkg => {
      pkg.pkg.version = newVersion;
      pkg.updateDependencies(newVersion);
      pkg.updateSimpleHTMLTokenizer(rootSimpleHTMLTokenizerVersion);

      if (!DRY_RUN) {
        pkg.savePackageJSON();
      }
      execWithSideEffects(`git add "${pkg.packageJSONPath}"`);
    });

  rootPkg.version = newVersion;
  if (!DRY_RUN) {
    fs.writeFileSync(rootPkgPath, JSON.stringify(rootPkg, null, 2));
      execWithSideEffects(`git add package.json`);
  }
}

function gitCommitAndTag() {
  execWithSideEffects(`git commit -m "Release v${newVersion}"`);
  execWithSideEffects(`git tag "v${newVersion}"`);
}

async function getOTPToken() {
  let token = await question(chalk.green('\nPlease provide OTP token '));

  return token.trim();
}

function publishPackage(distTag, otp, cwd) {
  execWithSideEffects(`npm publish --tag ${distTag} --access public --otp ${otp}`, {
    cwd
  });
}

async function confirmPublish() {
  console.log(chalk.blue("Version"), newVersion);

  let answer = await question(chalk.bgRed.white.bold("Are you sure? [Y/N]") + " ");

  if (answer !== 'y' && answer !== 'Y') {
    console.log(chalk.red("Aborting"));
    return;
  }

  let otp = await getOTPToken();

  let publicPackages = packages.filter(pkg => !pkg.private);
  for (let pkg of publicPackages) {
    let distTag = await autoDistTag(pkg.absolutePath);

    try {
      publishPackage(distTag, otp, pkg.absolutePath);
    } catch(e) {
      // the token is outdated, we need another one
      if (e.message.includes('E401') || e.message.includes('EOTP')) {
        otp = await getOTPToken();

        publishPackage(distTag, otp, pkg.absolutePath);
      } else {
        throw e;
      }
    }
  }

  execWithSideEffects(`git push origin master --tags`);

  console.log(chalk.green(`\nv${newVersion} deployed!`));
  console.log(chalk.green('Done.'));
}

function fatalError(message) {
  console.log(chalk.red(message));
  process.exit(1);
}

function throwNoPackagesErr() {
  console.log(chalk.red('No dist directory found. Did you do a build first? (npm run build)'))
  process.exit(1);
}

function assertDistExists() {
  try {
    let stat = fs.statSync(DIST_PATH);
    if (!stat.isDirectory()) {
      throwNoPackagesErr()
    }
  } catch (e) {
    throwNoPackagesErr();
  }
}

function assertGitIsClean() {
  let status = execSync('git status').toString();
  let force = process.argv.indexOf('--force') > -1;

  if (!status.match(/^nothing to commit/m)) {
    if (force) {
      console.log(chalk.yellow("--force"), "- ignoring unclean git working tree");
    } else {
      console.log(chalk.red("Git working tree isn't clean. Use --force to ignore this warning."));
      process.exit(1);
    }
  }
}

function assertPassesSmokeTest() {
  try {
    execSync('./bin/run-types-tests.js');
  } catch (err) {
    console.log(chalk.red("Types smoke test failed: "));
    console.log(err.stdout.toString());

    process.exit(1);
  }
}

function execWithSideEffects(cmd, options) {
  let cwd = '';
  if (options && options.cwd) {
    cwd = chalk.gray.dim(` (cwd: ${options.cwd}`);
  }

  console.log(chalk.green('>') + ' ' + chalk.gray(cmd) + cwd);
  if (!DRY_RUN) {
    return execSync.apply(null, arguments);
  }
}

function printPadded(table) {
  let maxWidth = Math.max(...table.map(r => r[0].length));
  table.forEach(row => {
    console.log(chalk.blue(pad(row[0], maxWidth)) + "  " + row[1]);
  })
}

function pad(string, width) {
  return string + " ".repeat(width - string.length);
}
<commit_msg>bin/publish: Remove unused `packageNames` variable<commit_after>#!/usr/bin/env node

const fs = require('fs');
const path = require('path');
const execSync = require('child_process').execSync;
const chalk = require('chalk');
const readline = require('readline');
const semver = require('semver');
const autoDistTag = require('auto-dist-tag');
const Project = require('../build/utils/project');

const DIST_PATH = path.resolve(__dirname, '../dist');
const PACKAGES_PATH = path.resolve(__dirname, '../packages');

const DRY_RUN = process.argv.indexOf('--dry-run') > -1;
if (DRY_RUN) {
  console.log(chalk.yellow("--dry-run"), "- side effects disabled");
}

// Fail fast if we haven't done a build first.
assertDistExists();
assertGitIsClean();
assertPassesSmokeTest();

let cli = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

// Load up the built packages in dist.
let packages = Project.from(DIST_PATH)
  .packages
  .filter(pkg => pkg.isPublishable);

let newVersion;

// Begin interactive CLI
printExistingVersions();
promptForVersion()
  .finally(() => cli.close())
  .catch(reason => {
    console.error(reason);
    process.exit(1);
  });

function question(prompt) {
  return new Promise(resolve => {
    cli.question(prompt, resolve);
  });
}

function printExistingVersions() {
  let packageVersions = packages.map(pkg => [pkg.name, pkg.version]);
  printPadded(packageVersions);
}

async function promptForVersion() {
  let defaultVersion = generateDefaultVersion();

  let version = await question(chalk.green(`\nNew version to publish? [${defaultVersion}] `));
  version = version.trim();
  if (version === '') {
    version = defaultVersion;
  }

  await validateNewVersion(version);
  console.log(chalk.green(`Publishing v${version}...`));

  newVersion = version;
  await applyNewVersion();
  await gitCommitAndTag();
  await confirmPublish();
}

function generateDefaultVersion() {
  let currentVersion = require('../package.json').version;
  return semver.inc(currentVersion, 'patch');
}

function validateNewVersion(version) {
  if (version === '') { fatalError("Version must not be empty."); }
  if (!semver.valid(version)) { fatalError("Version must be a valid SemVer version."); }

  packages.forEach(pkg => {
    if (!semver.gt(version, pkg.version)) {
      fatalError(`Version must be greater than existing versions. ${pkg.name} has version ${pkg.version}, which is greater than or equal to ${version}.`);
    }
  });
}

function applyNewVersion() {
  console.log(`Apply ${newVersion}`);

  // Update root package.json
  let rootPkgPath = path.join(__dirname, '../package.json');
  let rootPkg = JSON.parse(fs.readFileSync(rootPkgPath, 'utf8'));
  let rootSimpleHTMLTokenizerVersion = rootPkg.dependencies['simple-html-tokenizer'];

  // Update packages in the dist directory
  packages.forEach(pkg => {
    pkg.pkg.version = newVersion;
    pkg.updateDependencies(newVersion);
    pkg.updateSimpleHTMLTokenizer(rootSimpleHTMLTokenizerVersion);

    if (!DRY_RUN) {
      pkg.savePackageJSON();
    }
  });

  // Update source packages
  Project.from(PACKAGES_PATH)
    .packages
    .forEach(pkg => {
      pkg.pkg.version = newVersion;
      pkg.updateDependencies(newVersion);
      pkg.updateSimpleHTMLTokenizer(rootSimpleHTMLTokenizerVersion);

      if (!DRY_RUN) {
        pkg.savePackageJSON();
      }
      execWithSideEffects(`git add "${pkg.packageJSONPath}"`);
    });

  rootPkg.version = newVersion;
  if (!DRY_RUN) {
    fs.writeFileSync(rootPkgPath, JSON.stringify(rootPkg, null, 2));
      execWithSideEffects(`git add package.json`);
  }
}

function gitCommitAndTag() {
  execWithSideEffects(`git commit -m "Release v${newVersion}"`);
  execWithSideEffects(`git tag "v${newVersion}"`);
}

async function getOTPToken() {
  let token = await question(chalk.green('\nPlease provide OTP token '));

  return token.trim();
}

function publishPackage(distTag, otp, cwd) {
  execWithSideEffects(`npm publish --tag ${distTag} --access public --otp ${otp}`, {
    cwd
  });
}

async function confirmPublish() {
  console.log(chalk.blue("Version"), newVersion);

  let answer = await question(chalk.bgRed.white.bold("Are you sure? [Y/N]") + " ");

  if (answer !== 'y' && answer !== 'Y') {
    console.log(chalk.red("Aborting"));
    return;
  }

  let otp = await getOTPToken();

  let publicPackages = packages.filter(pkg => !pkg.private);
  for (let pkg of publicPackages) {
    let distTag = await autoDistTag(pkg.absolutePath);

    try {
      publishPackage(distTag, otp, pkg.absolutePath);
    } catch(e) {
      // the token is outdated, we need another one
      if (e.message.includes('E401') || e.message.includes('EOTP')) {
        otp = await getOTPToken();

        publishPackage(distTag, otp, pkg.absolutePath);
      } else {
        throw e;
      }
    }
  }

  execWithSideEffects(`git push origin master --tags`);

  console.log(chalk.green(`\nv${newVersion} deployed!`));
  console.log(chalk.green('Done.'));
}

function fatalError(message) {
  console.log(chalk.red(message));
  process.exit(1);
}

function throwNoPackagesErr() {
  console.log(chalk.red('No dist directory found. Did you do a build first? (npm run build)'))
  process.exit(1);
}

function assertDistExists() {
  try {
    let stat = fs.statSync(DIST_PATH);
    if (!stat.isDirectory()) {
      throwNoPackagesErr()
    }
  } catch (e) {
    throwNoPackagesErr();
  }
}

function assertGitIsClean() {
  let status = execSync('git status').toString();
  let force = process.argv.indexOf('--force') > -1;

  if (!status.match(/^nothing to commit/m)) {
    if (force) {
      console.log(chalk.yellow("--force"), "- ignoring unclean git working tree");
    } else {
      console.log(chalk.red("Git working tree isn't clean. Use --force to ignore this warning."));
      process.exit(1);
    }
  }
}

function assertPassesSmokeTest() {
  try {
    execSync('./bin/run-types-tests.js');
  } catch (err) {
    console.log(chalk.red("Types smoke test failed: "));
    console.log(err.stdout.toString());

    process.exit(1);
  }
}

function execWithSideEffects(cmd, options) {
  let cwd = '';
  if (options && options.cwd) {
    cwd = chalk.gray.dim(` (cwd: ${options.cwd}`);
  }

  console.log(chalk.green('>') + ' ' + chalk.gray(cmd) + cwd);
  if (!DRY_RUN) {
    return execSync.apply(null, arguments);
  }
}

function printPadded(table) {
  let maxWidth = Math.max(...table.map(r => r[0].length));
  table.forEach(row => {
    console.log(chalk.blue(pad(row[0], maxWidth)) + "  " + row[1]);
  })
}

function pad(string, width) {
  return string + " ".repeat(width - string.length);
}
<|endoftext|>


<commit_before>require("./core");
var types = require("../lib/types");
var def = types.Type.def;
var or = types.Type.or;
var builtin = types.builtInTypes;
var isString = builtin.string;
var isNumber = builtin.number;
var isBoolean = builtin.boolean;
var defaults = require("../lib/shared").defaults;

def("JSXAttribute")
    .bases("Node")
    .build("name", "value")
    .field("name", or(def("JSXIdentifier"), def("JSXNamespacedName")))
    .field("value", or(
        def("Literal"), // attr="value"
        def("JSXExpressionContainer"), // attr={value}
        null // attr= or just attr
    ), defaults["null"]);

def("JSXIdentifier")
    .bases("Identifier")
    .build("name")
    .field("name", isString);

def("JSXNamespacedName")
    .bases("Node")
    .build("namespace", "name")
    .field("namespace", def("JSXIdentifier"))
    .field("name", def("JSXIdentifier"));

def("JSXMemberExpression")
    .bases("MemberExpression")
    .build("object", "property")
    .field("object", or(def("JSXIdentifier"), def("JSXMemberExpression")))
    .field("property", def("JSXIdentifier"))
    .field("computed", isBoolean, defaults.false);

var JSXElementName = or(
    def("JSXIdentifier"),
    def("JSXNamespacedName"),
    def("JSXMemberExpression")
);

def("JSXSpreadAttribute")
    .bases("Node")
    .build("argument")
    .field("argument", def("Expression"));

var JSXAttributes = [or(
    def("JSXAttribute"),
    def("JSXSpreadAttribute")
)];

def("JSXExpressionContainer")
    .bases("Expression")
    .build("expression")
    .field("expression", def("Expression"));

def("JSXElement")
    .bases("Expression")
    .build("openingElement", "closingElement", "children")
    .field("openingElement", def("JSXOpeningElement"))
    .field("closingElement", or(def("JSXClosingElement"), null), defaults["null"])
    .field("children", [or(
        def("JSXElement"),
        def("JSXExpressionContainer"),
        def("JSXText"),
        def("Literal") // TODO Esprima should return JSXText instead.
    )], defaults.emptyArray)
    .field("name", JSXElementName, function() {
        // Little-known fact: the `this` object inside a default function
        // is none other than the partially-built object itself, and any
        // fields initialized directly from builder function arguments
        // (like openingElement, closingElement, and children) are
        // guaranteed to be available.
        return this.openingElement.name;
    })
    .field("selfClosing", isBoolean, function() {
        return this.openingElement.selfClosing;
    })
    .field("attributes", JSXAttributes, function() {
        return this.openingElement.attributes;
    });

def("JSXOpeningElement")
    .bases("Node") // TODO Does this make sense? Can't really be an JSXElement.
    .build("name", "attributes", "selfClosing")
    .field("name", JSXElementName)
    .field("attributes", JSXAttributes, defaults.emptyArray)
    .field("selfClosing", isBoolean, defaults["false"]);

def("JSXClosingElement")
    .bases("Node") // TODO Same concern.
    .build("name")
    .field("name", JSXElementName);

def("JSXText")
    .bases("Literal")
    .build("value")
    .field("value", isString);

def("JSXEmptyExpression").bases("Expression").build();

// Type Annotations
def("Type")
  .bases("Node");

def("AnyTypeAnnotation")
  .bases("Type");

def("VoidTypeAnnotation")
  .bases("Type");

def("NumberTypeAnnotation")
  .bases("Type");

def("NumberLiteralTypeAnnotation")
  .bases("Type")
  .build("value", "raw")
  .field("value", isNumber)
  .field("raw", isString);

def("StringTypeAnnotation")
  .bases("Type");

def("StringLiteralTypeAnnotation")
  .bases("Type")
  .build("value", "raw")
  .field("value", isString)
  .field("raw", isString);

def("BooleanTypeAnnotation")
  .bases("Type");

def("TypeAnnotation")
  .bases("Node")
  .build("typeAnnotation")
  .field("typeAnnotation", def("Type"));

def("NullableTypeAnnotation")
  .bases("Type")
  .build("typeAnnotation")
  .field("typeAnnotation", def("Type"));

def("FunctionTypeAnnotation")
  .bases("Type")
  .build("params", "returnType", "rest", "typeParameters")
  .field("params", [def("FunctionTypeParam")])
  .field("returnType", def("Type"))
  .field("rest", or(def("FunctionTypeParam"), null))
  .field("typeParameters", or(def("TypeParameterDeclaration"), null));

def("FunctionTypeParam")
  .bases("Node")
  .build("name", "typeAnnotation", "optional")
  .field("name", def("Identifier"))
  .field("typeAnnotation", def("Type"))
  .field("optional", isBoolean);

def("ArrayTypeAnnotation")
  .bases("Type")
  .build("elementType")
  .field("elementType", def("Type"));

def("ObjectTypeAnnotation")
  .bases("Type")
  .build("properties")
  .field("properties", [def("ObjectTypeProperty")])
  .field("indexers", [def("ObjectTypeIndexer")], defaults.emptyArray)
  .field("callProperties", [def("ObjectTypeCallProperty")], defaults.emptyArray);

def("ObjectTypeProperty")
  .bases("Node")
  .build("key", "value", "optional")
  .field("key", or(def("Literal"), def("Identifier")))
  .field("value", def("Type"))
  .field("optional", isBoolean);

def("ObjectTypeIndexer")
  .bases("Node")
  .build("id", "key", "value")
  .field("id", def("Identifier"))
  .field("key", def("Type"))
  .field("value", def("Type"));

def("ObjectTypeCallProperty")
  .bases("Node")
  .build("value")
  .field("value", def("FunctionTypeAnnotation"))
  .field("static", isBoolean, false);

def("QualifiedTypeIdentifier")
  .bases("Node")
  .build("qualification", "id")
  .field("qualification", or(def("Identifier"), def("QualifiedTypeIdentifier")))
  .field("id", def("Identifier"));

def("GenericTypeAnnotation")
  .bases("Type")
  .build("id", "typeParameters")
  .field("id", or(def("Identifier"), def("QualifiedTypeIdentifier")))
  .field("typeParameters", or(def("TypeParameterInstantiation"), null));

def("MemberTypeAnnotation")
  .bases("Type")
  .build("object", "property")
  .field("object", def("Identifier"))
  .field("property", or(def("MemberTypeAnnotation"), def("GenericTypeAnnotation")));

def("UnionTypeAnnotation")
  .bases("Type")
  .build("types")
  .field("types", [def("Type")]);

def("IntersectionTypeAnnotation")
  .bases("Type")
  .build("types")
  .field("types", [def("Type")]);

def("TypeofTypeAnnotation")
  .bases("Type")
  .build("argument")
  .field("argument", def("Type"));

def("Identifier")
  .field("typeAnnotation", or(def("TypeAnnotation"), null), defaults["null"]);

def("TypeParameterDeclaration")
  .bases("Node")
  .build("params")
  .field("params", [def("Identifier")]);

def("TypeParameterInstantiation")
  .bases("Node")
  .build("params")
  .field("params", [def("Type")]);

def("Function")
  .field("returnType", or(def("TypeAnnotation"), null), defaults["null"])
  .field("typeParameters", or(def("TypeParameterDeclaration"), null), defaults["null"]);

def("ClassProperty")
  .build("key", "typeAnnotation")
  .field("typeAnnotation", def("TypeAnnotation"))
  .field("static", isBoolean, false);

def("ClassImplements")
  .field("typeParameters", or(def("TypeParameterInstantiation"), null), defaults["null"]);

def("InterfaceDeclaration")
  .bases("Statement")
  .build("id", "body", "extends")
  .field("id", def("Identifier"))
  .field("typeParameters", or(def("TypeParameterDeclaration"), null), defaults["null"])
  .field("body", def("ObjectTypeAnnotation"))
  .field("extends", [def("InterfaceExtends")]);

def("InterfaceExtends")
  .bases("Node")
  .build("id")
  .field("id", def("Identifier"))
  .field("typeParameters", or(def("TypeParameterInstantiation"), null));

def("TypeAlias")
  .bases("Statement")
  .build("id", "typeParameters", "right")
  .field("id", def("Identifier"))
  .field("typeParameters", or(def("TypeParameterDeclaration"), null))
  .field("right", def("Type"));

def("TypeCastExpression")
  .bases("Expression")
  .build("expression", "typeAnnotation")
  .field("expression", def("Expression"))
  .field("typeAnnotation", def("TypeAnnotation"));

def("TupleTypeAnnotation")
  .bases("Type")
  .build("types")
  .field("types", [def("Type")]);

def("DeclareVariable")
  .bases("Statement")
  .build("id")
  .field("id", def("Identifier"));

def("DeclareFunction")
  .bases("Statement")
  .build("id")
  .field("id", def("Identifier"));

def("DeclareClass")
  .bases("InterfaceDeclaration")
  .build("id");

def("DeclareModule")
  .bases("Statement")
  .build("id", "body")
  .field("id", or(def("Identifier"), def("Literal")))
  .field("body", def("BlockStatement"));
<commit_msg>Add BooleanLiteralTypeAnnotation to fb-harmony<commit_after>require("./core");
var types = require("../lib/types");
var def = types.Type.def;
var or = types.Type.or;
var builtin = types.builtInTypes;
var isString = builtin.string;
var isNumber = builtin.number;
var isBoolean = builtin.boolean;
var defaults = require("../lib/shared").defaults;

def("JSXAttribute")
    .bases("Node")
    .build("name", "value")
    .field("name", or(def("JSXIdentifier"), def("JSXNamespacedName")))
    .field("value", or(
        def("Literal"), // attr="value"
        def("JSXExpressionContainer"), // attr={value}
        null // attr= or just attr
    ), defaults["null"]);

def("JSXIdentifier")
    .bases("Identifier")
    .build("name")
    .field("name", isString);

def("JSXNamespacedName")
    .bases("Node")
    .build("namespace", "name")
    .field("namespace", def("JSXIdentifier"))
    .field("name", def("JSXIdentifier"));

def("JSXMemberExpression")
    .bases("MemberExpression")
    .build("object", "property")
    .field("object", or(def("JSXIdentifier"), def("JSXMemberExpression")))
    .field("property", def("JSXIdentifier"))
    .field("computed", isBoolean, defaults.false);

var JSXElementName = or(
    def("JSXIdentifier"),
    def("JSXNamespacedName"),
    def("JSXMemberExpression")
);

def("JSXSpreadAttribute")
    .bases("Node")
    .build("argument")
    .field("argument", def("Expression"));

var JSXAttributes = [or(
    def("JSXAttribute"),
    def("JSXSpreadAttribute")
)];

def("JSXExpressionContainer")
    .bases("Expression")
    .build("expression")
    .field("expression", def("Expression"));

def("JSXElement")
    .bases("Expression")
    .build("openingElement", "closingElement", "children")
    .field("openingElement", def("JSXOpeningElement"))
    .field("closingElement", or(def("JSXClosingElement"), null), defaults["null"])
    .field("children", [or(
        def("JSXElement"),
        def("JSXExpressionContainer"),
        def("JSXText"),
        def("Literal") // TODO Esprima should return JSXText instead.
    )], defaults.emptyArray)
    .field("name", JSXElementName, function() {
        // Little-known fact: the `this` object inside a default function
        // is none other than the partially-built object itself, and any
        // fields initialized directly from builder function arguments
        // (like openingElement, closingElement, and children) are
        // guaranteed to be available.
        return this.openingElement.name;
    })
    .field("selfClosing", isBoolean, function() {
        return this.openingElement.selfClosing;
    })
    .field("attributes", JSXAttributes, function() {
        return this.openingElement.attributes;
    });

def("JSXOpeningElement")
    .bases("Node") // TODO Does this make sense? Can't really be an JSXElement.
    .build("name", "attributes", "selfClosing")
    .field("name", JSXElementName)
    .field("attributes", JSXAttributes, defaults.emptyArray)
    .field("selfClosing", isBoolean, defaults["false"]);

def("JSXClosingElement")
    .bases("Node") // TODO Same concern.
    .build("name")
    .field("name", JSXElementName);

def("JSXText")
    .bases("Literal")
    .build("value")
    .field("value", isString);

def("JSXEmptyExpression").bases("Expression").build();

// Type Annotations
def("Type")
  .bases("Node");

def("AnyTypeAnnotation")
  .bases("Type");

def("VoidTypeAnnotation")
  .bases("Type");

def("NumberTypeAnnotation")
  .bases("Type");

def("NumberLiteralTypeAnnotation")
  .bases("Type")
  .build("value", "raw")
  .field("value", isNumber)
  .field("raw", isString);

def("StringTypeAnnotation")
  .bases("Type");

def("StringLiteralTypeAnnotation")
  .bases("Type")
  .build("value", "raw")
  .field("value", isString)
  .field("raw", isString);

def("BooleanTypeAnnotation")
  .bases("Type");

def("BooleanLiteralTypeAnnotation")
  .bases("Type")
  .build("value", "raw")
  .field("value", isBoolean)
  .field("raw", isString);

def("TypeAnnotation")
  .bases("Node")
  .build("typeAnnotation")
  .field("typeAnnotation", def("Type"));

def("NullableTypeAnnotation")
  .bases("Type")
  .build("typeAnnotation")
  .field("typeAnnotation", def("Type"));

def("FunctionTypeAnnotation")
  .bases("Type")
  .build("params", "returnType", "rest", "typeParameters")
  .field("params", [def("FunctionTypeParam")])
  .field("returnType", def("Type"))
  .field("rest", or(def("FunctionTypeParam"), null))
  .field("typeParameters", or(def("TypeParameterDeclaration"), null));

def("FunctionTypeParam")
  .bases("Node")
  .build("name", "typeAnnotation", "optional")
  .field("name", def("Identifier"))
  .field("typeAnnotation", def("Type"))
  .field("optional", isBoolean);

def("ArrayTypeAnnotation")
  .bases("Type")
  .build("elementType")
  .field("elementType", def("Type"));

def("ObjectTypeAnnotation")
  .bases("Type")
  .build("properties")
  .field("properties", [def("ObjectTypeProperty")])
  .field("indexers", [def("ObjectTypeIndexer")], defaults.emptyArray)
  .field("callProperties", [def("ObjectTypeCallProperty")], defaults.emptyArray);

def("ObjectTypeProperty")
  .bases("Node")
  .build("key", "value", "optional")
  .field("key", or(def("Literal"), def("Identifier")))
  .field("value", def("Type"))
  .field("optional", isBoolean);

def("ObjectTypeIndexer")
  .bases("Node")
  .build("id", "key", "value")
  .field("id", def("Identifier"))
  .field("key", def("Type"))
  .field("value", def("Type"));

def("ObjectTypeCallProperty")
  .bases("Node")
  .build("value")
  .field("value", def("FunctionTypeAnnotation"))
  .field("static", isBoolean, false);

def("QualifiedTypeIdentifier")
  .bases("Node")
  .build("qualification", "id")
  .field("qualification", or(def("Identifier"), def("QualifiedTypeIdentifier")))
  .field("id", def("Identifier"));

def("GenericTypeAnnotation")
  .bases("Type")
  .build("id", "typeParameters")
  .field("id", or(def("Identifier"), def("QualifiedTypeIdentifier")))
  .field("typeParameters", or(def("TypeParameterInstantiation"), null));

def("MemberTypeAnnotation")
  .bases("Type")
  .build("object", "property")
  .field("object", def("Identifier"))
  .field("property", or(def("MemberTypeAnnotation"), def("GenericTypeAnnotation")));

def("UnionTypeAnnotation")
  .bases("Type")
  .build("types")
  .field("types", [def("Type")]);

def("IntersectionTypeAnnotation")
  .bases("Type")
  .build("types")
  .field("types", [def("Type")]);

def("TypeofTypeAnnotation")
  .bases("Type")
  .build("argument")
  .field("argument", def("Type"));

def("Identifier")
  .field("typeAnnotation", or(def("TypeAnnotation"), null), defaults["null"]);

def("TypeParameterDeclaration")
  .bases("Node")
  .build("params")
  .field("params", [def("Identifier")]);

def("TypeParameterInstantiation")
  .bases("Node")
  .build("params")
  .field("params", [def("Type")]);

def("Function")
  .field("returnType", or(def("TypeAnnotation"), null), defaults["null"])
  .field("typeParameters", or(def("TypeParameterDeclaration"), null), defaults["null"]);

def("ClassProperty")
  .build("key", "typeAnnotation")
  .field("typeAnnotation", def("TypeAnnotation"))
  .field("static", isBoolean, false);

def("ClassImplements")
  .field("typeParameters", or(def("TypeParameterInstantiation"), null), defaults["null"]);

def("InterfaceDeclaration")
  .bases("Statement")
  .build("id", "body", "extends")
  .field("id", def("Identifier"))
  .field("typeParameters", or(def("TypeParameterDeclaration"), null), defaults["null"])
  .field("body", def("ObjectTypeAnnotation"))
  .field("extends", [def("InterfaceExtends")]);

def("InterfaceExtends")
  .bases("Node")
  .build("id")
  .field("id", def("Identifier"))
  .field("typeParameters", or(def("TypeParameterInstantiation"), null));

def("TypeAlias")
  .bases("Statement")
  .build("id", "typeParameters", "right")
  .field("id", def("Identifier"))
  .field("typeParameters", or(def("TypeParameterDeclaration"), null))
  .field("right", def("Type"));

def("TypeCastExpression")
  .bases("Expression")
  .build("expression", "typeAnnotation")
  .field("expression", def("Expression"))
  .field("typeAnnotation", def("TypeAnnotation"));

def("TupleTypeAnnotation")
  .bases("Type")
  .build("types")
  .field("types", [def("Type")]);

def("DeclareVariable")
  .bases("Statement")
  .build("id")
  .field("id", def("Identifier"));

def("DeclareFunction")
  .bases("Statement")
  .build("id")
  .field("id", def("Identifier"));

def("DeclareClass")
  .bases("InterfaceDeclaration")
  .build("id");

def("DeclareModule")
  .bases("Statement")
  .build("id", "body")
  .field("id", or(def("Identifier"), def("Literal")))
  .field("body", def("BlockStatement"));
<|endoftext|>


<commit_before>import React from 'react'
import ReactDOM from 'react-dom'
import Tabs from '../../components/Tabs'
import Bar from '../../components/Bar'
import Page from '../../components/Page'
import ContextMenu from '../../components/ContextMenu'
import MenuItem from '../../components/MenuItem'

import '../../helpers/Arrays'

import '../../app.scss'

const {remote, clipboard} = require('electron')
const fs = require('fs')
const path = require('path')
const homedir = require('os').homedir()
const nativeImage = remote.nativeImage

const userData = path.join(homedir, '.wexond')

const requiredFiles = [
  {
    path: 'history.json',
    defaultContent: '[]'
  }
]

window.global = {
  currentWindow: remote.getCurrentWindow(),
  remote: remote,
  clipboard: clipboard,
  tabs: [],
  pages: [],
  tabsData: {
    pinnedTabWidth: 32,
    maxTabWidth: 190,
    newTabWidth: 32
  },
  tabsAnimationData: {
    positioningDuration: 0.2,
    hoverDuration: 0.2,
    positioningEasing: 'cubic-bezier(0.215, 0.61, 0.355, 1)'
  },
  defaultOptions: {
    select: true,
    url: 'wexond://newtab'
  },
  excludedURLs: ['wexond://newtab'],
  historyPath: path.join(userData, 'history.json'),
  systembarHeight: 32
}

if (!fs.existsSync(userData)) {
  fs.mkdir(userData)
}

for (var i = 0; i < requiredFiles.length; i++) {
  let file = path.join(userData, requiredFiles[i].path)

  if (!fs.existsSync(file)) {
    fs.writeFile(file, requiredFiles[i].defaultContent, function (err) {
      if (err) console.error(err)
    })
  }
}

class App extends React.Component {
  constructor () {
    super()

    this.state = {
      pagesToCreate: []
    }

    this.cursor = {}
  }

  componentDidMount () {
    const self = this

    this.tabs.tabbar.addEventListener('contextmenu', function (e) {
      if (e.target !== self.tabs.addButton) {
        self.tabMenu.show()
        self.webviewMenu.hide()

        let left = e.pageX + 1
        let top = e.pageY + 1

        if (left + 300 > window.innerWidth) {
          left = e.pageX - 301
        }
        if (top + self.tabMenu.state.height > window.innerHeight) {
          top = e.pageY - self.tabMenu.state.height
        }
        if (top < 0) {
          top = 96
        }

        self.tabMenu.setState({left: left, top: top})
      }
    })

    window.addEventListener('click', function () {
      self.tabMenu.hide()
      self.webviewMenu.hide()
    })

    this.webviewMenu.setState(
      {
        menuItems: [
          {
            title: 'Open link in new tab',
            type: 'menu-item',
            show: false,
            onClick: function () {
              let data = self.WCMData
              self.tabs.addTab({select: false, url: data.linkURL})
            }
          },
          {
            type: 'separator',
            show: false
          },
          {
            title: 'Copy link address',
            type: 'menu-item',
            show: false,
            onClick: function () {
              let data = self.WCMData
              clipboard.clear()
              clipboard.writeText(data.linkURL)
            }
          },
          {
            title: 'Save link as',
            type: 'menu-item',
            show: false
          },
          {
            type: 'separator',
            show: false
          },
          {
            title: 'Open image in new tab',
            type: 'menu-item',
            show: false,
            onClick: function () {
              let data = self.WCMData
              self.tabs.addTab({select: false, url: data.srcURL})
            }
          },
          {
            title: 'Save image as',
            type: 'menu-item',
            show: false
          },
          {
            title: 'Copy image',
            type: 'menu-item',
            show: false,
            onClick: function () {
              let data = self.WCMData
              clipboard.clear()
              let img = nativeImage.createFromDataURL(data.srcURL)
              clipboard.writeImage(img)
            }
          },
          {
            title: 'Copy image address',
            type: 'menu-item',
            show: false,
            onClick: function () {
              let data = self.WCMData
              clipboard.clear()
              clipboard.writeText(data.srcURL)
            }
          },
          {
            type: 'separator',
            show: false
          },
          {
            title: 'Print',
            type: 'menu-item',
            show: false,
            onClick: function () {
              self.getSelectedPage().webview.print()
            }
          },
          {
            title: 'Save as',
            type: 'menu-item',
            show: false
          },
          {
            type: 'separator',
            show: false
          },
          {
            title: 'View source',
            type: 'menu-item',
            show: false
          },
          {
            title: 'Inspect element',
            type: 'menu-item',
            show: true,
            onClick: function () {
              let data = self.WCMData
              self.getSelectedPage().webview.inspectElement(data.x, data.y)
            }
          }
        ]
      }
    )

    this.tabMenu.setState(
      {
        menuItems: [
          {
            title: 'Add new tab',
            type: 'menu-item'
          },
          {
            type: 'separator'
          },
          {
            title: 'Pin tab',
            type: 'menu-item'
          },
          {
            title: 'Mute tab',
            type: 'menu-item'
          },
          {
            title: 'Duplicate',
            type: 'menu-item'
          },
          {
            type: 'separator'
          },
          {
            title: 'Close tab',
            type: 'menu-item'
          },
          {
            title: 'Close other tabs',
            type: 'menu-item'
          },
          {
            title: 'Close tabs from left',
            type: 'menu-item'
          },
          {
            title: 'Close tabs from right',
            type: 'menu-item'
          },
          {
            title: 'Revert closed tab',
            type: 'menu-item'
          }
        ]
      }
    )
  }

  /**
   * Sets bar text.
   * @param {string} text
   * @param {boolean} overrideActive
   */
  updateBarText = (text, overrideActive = false) => {
    const bar = this.bar
    let contains = false
    // Check if the url from webview is in excluded URLs.
    for (var i = 0; i < global.excludedURLs.length; i++) {
      if (global.excludedURLs[i].indexOf(text) !== -1) {
        contains = true
        break
      }
      if (text.indexOf(global.excludedURLs[i]) !== -1) {
        contains = true
        break
      }
    }
    // check if webview's url is in excluded urls
    if (!contains) {
      // if not, set bar's text to webview's url and unlock bar
      bar.setText(text, overrideActive)
      bar.locked = false
      bar.tempLocked = false
    } else {
      bar.setText('', overrideActive)
      // if it is, check if the url is wexond://newtab
      if (text.startsWith('wexond://newtab')) {
        // if it is, lock and show bar
        bar.locked = true
        bar.show()
      }
    }
  }

  /**
   * Gets this {App}.
   * @return {App}
   */
  getApp = () => {
    return this
  }

  /**
   * Gets selected page.
   * @return {Page}
   */
  getSelectedPage = () => {
    for (var i = 0; i < global.pages.length; i++) {
      if (global.pages[i].props.getTab().selected) {
        return global.pages[i]
      }
    }
    return null
  }

  render () {
    const self = this

    return (

        <Tabs ref={(r) => { this.tabs = r }} getApp={this.getApp} />
        <Bar ref={(r) => { this.bar = r }} getApp={this.getApp} />
        {this.state.pagesToCreate.map((data, key) => {
          return (
            <Page getApp={this.getApp} getTab={data.getTab} url={data.url} key={key} />
          )
        })}
        <ContextMenu getApp={this.getApp} ref={(r) => { this.webviewMenu = r }} />
        <ContextMenu getApp={this.getApp} ref={(r) => { this.tabMenu = r }} />

    )
  }
}

ReactDOM.render(<App />, document.getElementById('app'))
<commit_msg>Add Save as functionality in webview context menu<commit_after>import React from 'react'
import ReactDOM from 'react-dom'
import Tabs from '../../components/Tabs'
import Bar from '../../components/Bar'
import Page from '../../components/Page'
import ContextMenu from '../../components/ContextMenu'
import MenuItem from '../../components/MenuItem'

import '../../helpers/Arrays'

import '../../app.scss'

const {remote, clipboard} = require('electron')
const {dialog, nativeImage} = remote
const fs = require('fs')
const path = require('path')
const homedir = require('os').homedir()

const userData = path.join(homedir, '.wexond')

const requiredFiles = [
  {
    path: 'history.json',
    defaultContent: '[]'
  }
]

window.global = {
  currentWindow: remote.getCurrentWindow(),
  remote: remote,
  clipboard: clipboard,
  tabs: [],
  pages: [],
  tabsData: {
    pinnedTabWidth: 32,
    maxTabWidth: 190,
    newTabWidth: 32
  },
  tabsAnimationData: {
    positioningDuration: 0.2,
    hoverDuration: 0.2,
    positioningEasing: 'cubic-bezier(0.215, 0.61, 0.355, 1)'
  },
  defaultOptions: {
    select: true,
    url: 'wexond://newtab'
  },
  excludedURLs: ['wexond://newtab'],
  historyPath: path.join(userData, 'history.json'),
  systembarHeight: 32
}

if (!fs.existsSync(userData)) {
  fs.mkdir(userData)
}

for (var i = 0; i < requiredFiles.length; i++) {
  let file = path.join(userData, requiredFiles[i].path)

  if (!fs.existsSync(file)) {
    fs.writeFile(file, requiredFiles[i].defaultContent, function (err) {
      if (err) console.error(err)
    })
  }
}

class App extends React.Component {
  constructor () {
    super()

    this.state = {
      pagesToCreate: []
    }

    this.cursor = {}
  }

  componentDidMount () {
    const self = this

    this.tabs.tabbar.addEventListener('contextmenu', function (e) {
      if (e.target !== self.tabs.addButton) {
        self.tabMenu.show()
        self.webviewMenu.hide()

        let left = e.pageX + 1
        let top = e.pageY + 1

        if (left + 300 > window.innerWidth) {
          left = e.pageX - 301
        }
        if (top + self.tabMenu.state.height > window.innerHeight) {
          top = e.pageY - self.tabMenu.state.height
        }
        if (top < 0) {
          top = 96
        }

        self.tabMenu.setState({left: left, top: top})
      }
    })

    window.addEventListener('click', function () {
      self.tabMenu.hide()
      self.webviewMenu.hide()
    })

    this.webviewMenu.setState(
      {
        menuItems: [
          {
            title: 'Open link in new tab',
            type: 'menu-item',
            show: false,
            onClick: function () {
              let data = self.WCMData
              self.tabs.addTab({select: false, url: data.linkURL})
            }
          },
          {
            type: 'separator',
            show: false
          },
          {
            title: 'Copy link address',
            type: 'menu-item',
            show: false,
            onClick: function () {
              let data = self.WCMData
              clipboard.clear()
              clipboard.writeText(data.linkURL)
            }
          },
          {
            title: 'Save link as',
            type: 'menu-item',
            show: false
          },
          {
            type: 'separator',
            show: false
          },
          {
            title: 'Open image in new tab',
            type: 'menu-item',
            show: false,
            onClick: function () {
              let data = self.WCMData
              self.tabs.addTab({select: false, url: data.srcURL})
            }
          },
          {
            title: 'Save image as',
            type: 'menu-item',
            show: false
          },
          {
            title: 'Copy image',
            type: 'menu-item',
            show: false,
            onClick: function () {
              let data = self.WCMData
              clipboard.clear()
              let img = nativeImage.createFromDataURL(data.srcURL)
              clipboard.writeImage(img)
            }
          },
          {
            title: 'Copy image address',
            type: 'menu-item',
            show: false,
            onClick: function () {
              let data = self.WCMData
              clipboard.clear()
              clipboard.writeText(data.srcURL)
            }
          },
          {
            type: 'separator',
            show: false
          },
          {
            title: 'Print',
            type: 'menu-item',
            show: false,
            onClick: function () {
              self.getSelectedPage().webview.print()
            }
          },
          {
            title: 'Save as',
            type: 'menu-item',
            show: false,
            onClick: function () {
              dialog.showSaveDialog(
                {
                  filters: [
                    {
                      name: 'HTML file',
                      extensions: ['html']
                    }
                  ]
                },
                function (path) {
                  self.getSelectedPage().webview.getWebContents().savePage(path, 'HTMLComplete', (error) => {
                    if (error) console.error(error)
                  })
                }
              )
            }
          },
          {
            type: 'separator',
            show: false
          },
          {
            title: 'View source',
            type: 'menu-item',
            show: false
          },
          {
            title: 'Inspect element',
            type: 'menu-item',
            show: true,
            onClick: function () {
              let data = self.WCMData
              self.getSelectedPage().webview.inspectElement(data.x, data.y)
            }
          }
        ]
      }
    )

    this.tabMenu.setState(
      {
        menuItems: [
          {
            title: 'Add new tab',
            type: 'menu-item'
          },
          {
            type: 'separator'
          },
          {
            title: 'Pin tab',
            type: 'menu-item'
          },
          {
            title: 'Mute tab',
            type: 'menu-item'
          },
          {
            title: 'Duplicate',
            type: 'menu-item'
          },
          {
            type: 'separator'
          },
          {
            title: 'Close tab',
            type: 'menu-item'
          },
          {
            title: 'Close other tabs',
            type: 'menu-item'
          },
          {
            title: 'Close tabs from left',
            type: 'menu-item'
          },
          {
            title: 'Close tabs from right',
            type: 'menu-item'
          },
          {
            title: 'Revert closed tab',
            type: 'menu-item'
          }
        ]
      }
    )
  }

  /**
   * Sets bar text.
   * @param {string} text
   * @param {boolean} overrideActive
   */
  updateBarText = (text, overrideActive = false) => {
    const bar = this.bar
    let contains = false
    // Check if the url from webview is in excluded URLs.
    for (var i = 0; i < global.excludedURLs.length; i++) {
      if (global.excludedURLs[i].indexOf(text) !== -1) {
        contains = true
        break
      }
      if (text.indexOf(global.excludedURLs[i]) !== -1) {
        contains = true
        break
      }
    }
    // check if webview's url is in excluded urls
    if (!contains) {
      // if not, set bar's text to webview's url and unlock bar
      bar.setText(text, overrideActive)
      bar.locked = false
      bar.tempLocked = false
    } else {
      bar.setText('', overrideActive)
      // if it is, check if the url is wexond://newtab
      if (text.startsWith('wexond://newtab')) {
        // if it is, lock and show bar
        bar.locked = true
        bar.show()
      }
    }
  }

  /**
   * Gets this {App}.
   * @return {App}
   */
  getApp = () => {
    return this
  }

  /**
   * Gets selected page.
   * @return {Page}
   */
  getSelectedPage = () => {
    for (var i = 0; i < global.pages.length; i++) {
      if (global.pages[i].props.getTab().selected) {
        return global.pages[i]
      }
    }
    return null
  }

  render () {
    const self = this

    return (

        <Tabs ref={(r) => { this.tabs = r }} getApp={this.getApp} />
        <Bar ref={(r) => { this.bar = r }} getApp={this.getApp} />
        {this.state.pagesToCreate.map((data, key) => {
          return (
            <Page getApp={this.getApp} getTab={data.getTab} url={data.url} key={key} />
          )
        })}
        <ContextMenu getApp={this.getApp} ref={(r) => { this.webviewMenu = r }} />
        <ContextMenu getApp={this.getApp} ref={(r) => { this.tabMenu = r }} />

    )
  }
}

ReactDOM.render(<App />, document.getElementById('app'))
<|endoftext|>