<commit_before>// <browser>

var Temple = {};

Temple.VERSION = '0.1.0-git';

/*
Operator table from RFC 6570, Appendix A, Implementation Hints.

.------------------------------------------------------------------.
|          NUL     +      .       /       ;      ?      &      #   |
|------------------------------------------------------------------|
| first |  ""     ""     "."     "/"     ";"    "?"    "&"    "#"  |
| sep   |  ","    ","    "."     "/"     ";"    "&"    "&"    ","  |
| named | false  false  false   false   true   true   true   false |
| ifemp |  ""     ""     ""      ""      ""     "="    "="    ""   |
| allow |   U     U+R     U       U       U      U      U     U+R  |
`------------------------------------------------------------------'

first:  String to append to the result first if any of the expression's
        variables are defined.

sep:    Separator to append to the result before any second (or subsequent)
        defined variable expansion.

named:  A boolean for whether or not the expansion includes the variable or
        key name when no explode modifier is given.

ifemp:  String to append to the name if the corresponding value is empty.

allow:  What characters to allow unencoded within the value expansion.
        'U' means any character not in the unreserved list will be encoded.
        'U+R' means any character not in the unreserved or reserved lists will
        be encoded.
*/

Temple.Operator = {
    /**
    Returns the operator configuration for the given operator name.

    @method get
    @param {String} Operator name.
    @return {Object} Operator configuration.
    **/
    get: function (op) {
        return this[op] || this['default'];
    },

    // Simple string expansion
    'default': {
        first: '',
        sep  : ',',
        named: false,
        ifemp: '',
        allow: 'U'
    },

    // Reserved expansion
    '+': {
        first: '',
        sep  : ',',
        named: false,
        ifemp: '',
        allow: 'U+R'
    },

    // Label expansion with dot-prefix
    '.': {
        first : '.',
        sep   : '.',
        named : false,
        ifemp : '',
        allow : 'U'
    },

    // Path segment expansion
    '/': {
        first: '/',
        sep  : '/',
        named: false,
        ifemp: '',
        allow: 'U'
    },

    // Path-style parameter expansion
    ';': {
        first: ';',
        sep  : ';',
        named: true,
        ifemp: '',
        allow: 'U'
    },

    // Form-style query expansion
    '?': {
        first: '?',
        sep  : '&',
        named: true,
        ifemp: '=',
        allow: 'U'
    },

    // Form-style query continuation
    '&': {
        first: '&',
        sep  : '&',
        named: true,
        ifemp: '=',
        allow: 'U'
    },

    // Fragment expansion
    '#': {
        first : '#',
        sep   : ',',
        named : false,
        ifemp : '',
        allow : 'U+R'
    }
};

(function () {
var Aproto = Array.prototype,

    // Regular expressions used for encoding expansion values.
    re  = {
        // Match characters not in the unreserved list.
        U: /[^\w~.\-]/g,

        // Match characters not in the ureserved or reserved lists.
        UR: /[^\w.~:\/\?#\[\]@!\$&'()*+,;=\-]/g,

        // Match invalid URI characters, and "%" characters not part of a
        // percent-encoded character. Percent-encoding detection can produce
        // false positives.
        L: /[\x01-\x1A]|[ "'<>\\\^`{|}]|%(?![a-fA-F0-9]{2})/g
    };

// Escape additional URI characters per RFC 3986: !, ', (, ), *
function _encodeURIComponent(str) {
    return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27')
        .replace(/\(/g, '%27').replace(/\)/g, '%28').replace(/\*/, '%2A');
}

/**
URL-encode portions of a value that match a regular expression. Optionally
avoid double-encoding of already URL-encoded values. Non-string values are
coerced to string before encoding.

@method encode
@param {Mixed} value The value to encode.
@param {RegExp} re Regular expression used for matching.
@param {Boolean} [decode] Whether or not to decode the string first, to
    prevent double-encoding. Default is false.
@return {String} The URI encoded string.
**/
function encode(value, re, decode) {
    var string = '' + value,
        m;

    if (decode && /\%/.test(string)) {
        try {
            string = decodeURIComponent(string);
        } catch (e) {}
    }

    return (function next(start) {
        if (m = re.exec(string)) {
            return string.slice(start, m.index) + _encodeURIComponent(m[0]) +
                next(m.index + 1);
        }

        return string.substring(start);
    }(0));
}

Temple.Encoder = {
    varname   : encodeURIComponent,
    literal   : function (value) { return encode(value, re.L);        },
    unreserved: function (value) { return encode(value, re.U,  true); },
    reserved  : function (value) { return encode(value, re.UR, true); }
};

// Shorthand aliases.
Temple.Encoder.L      = Temple.Encoder.literal;
Temple.Encoder.U      = Temple.Encoder.unreserved;
Temple.Encoder['U+R'] = Temple.Encoder.reserved;

Temple.Array = {
    each: Aproto.forEach ? function (array, fn, thisObj) {
        array.forEach(fn, thisObj);
    } : function (array, fn, thisObj) {
        var i, len;

        for (i = 0, len = (array && array.length) || 0; i < len; ++i) {
            if (array[i] !== undefined) {
                fn.call(thisObj, array[i], i, array);
            }
        }
    },

    filter: Aproto.filter ? function (array, fn, thisObj) {
        return array.filter(fn, thisObj);
    } : function (array, fn, thisObj) {
        var results = [];

        Temple.Array.each(array, function (item, index) {
            if (fn.call(thisObj, item, index, array)) {
                results.push(item);
            }
        });

        return results;
    },

    isArray: Array.isArray ? Array.isArray : function (o) {
        return Object.prototype.toString.call(o) === '[object Array]';
    },

    map: Aproto.map ? function (array, fn, thisObj) {
        return array.map(fn, thisObj);
    } : function (array, fn, thisObj) {
        var results = array.concat();

        Temple.Array.each(array, function (item, index) {
            results = fn.call(thisObj, item, index, array);
        }, thisObj);

        return results;
    }
};

Temple.Object = {
    each: function (o, fn, thisObj) {
        var key;

        for (key in o) {
            if (o.hasOwnProperty(key)) {
                fn.call(thisObj, o[key], key, o);
            }
        }
    },

    some: function (o, fn, thisObj) {
        var key;

        for (key in o) {
            if (o.hasOwnProperty(key) && fn.call(thisObj, o[key], key, o)) {
                return true;
            }
        }

        return false;
    }
};

}());

// </browser>

module.exports = Temple;
<commit_msg>URI-encode parentheses to the correct values<commit_after>// <browser>

var Temple = {};

Temple.VERSION = '0.1.0-git';

/*
Operator table from RFC 6570, Appendix A, Implementation Hints.

.------------------------------------------------------------------.
|          NUL     +      .       /       ;      ?      &      #   |
|------------------------------------------------------------------|
| first |  ""     ""     "."     "/"     ";"    "?"    "&"    "#"  |
| sep   |  ","    ","    "."     "/"     ";"    "&"    "&"    ","  |
| named | false  false  false   false   true   true   true   false |
| ifemp |  ""     ""     ""      ""      ""     "="    "="    ""   |
| allow |   U     U+R     U       U       U      U      U     U+R  |
`------------------------------------------------------------------'

first:  String to append to the result first if any of the expression's
        variables are defined.

sep:    Separator to append to the result before any second (or subsequent)
        defined variable expansion.

named:  A boolean for whether or not the expansion includes the variable or
        key name when no explode modifier is given.

ifemp:  String to append to the name if the corresponding value is empty.

allow:  What characters to allow unencoded within the value expansion.
        'U' means any character not in the unreserved list will be encoded.
        'U+R' means any character not in the unreserved or reserved lists will
        be encoded.
*/

Temple.Operator = {
    /**
    Returns the operator configuration for the given operator name.

    @method get
    @param {String} Operator name.
    @return {Object} Operator configuration.
    **/
    get: function (op) {
        return this[op] || this['default'];
    },

    // Simple string expansion
    'default': {
        first: '',
        sep  : ',',
        named: false,
        ifemp: '',
        allow: 'U'
    },

    // Reserved expansion
    '+': {
        first: '',
        sep  : ',',
        named: false,
        ifemp: '',
        allow: 'U+R'
    },

    // Label expansion with dot-prefix
    '.': {
        first : '.',
        sep   : '.',
        named : false,
        ifemp : '',
        allow : 'U'
    },

    // Path segment expansion
    '/': {
        first: '/',
        sep  : '/',
        named: false,
        ifemp: '',
        allow: 'U'
    },

    // Path-style parameter expansion
    ';': {
        first: ';',
        sep  : ';',
        named: true,
        ifemp: '',
        allow: 'U'
    },

    // Form-style query expansion
    '?': {
        first: '?',
        sep  : '&',
        named: true,
        ifemp: '=',
        allow: 'U'
    },

    // Form-style query continuation
    '&': {
        first: '&',
        sep  : '&',
        named: true,
        ifemp: '=',
        allow: 'U'
    },

    // Fragment expansion
    '#': {
        first : '#',
        sep   : ',',
        named : false,
        ifemp : '',
        allow : 'U+R'
    }
};

(function () {
var Aproto = Array.prototype,

    // Regular expressions used for encoding expansion values.
    re  = {
        // Match characters not in the unreserved list.
        U: /[^\w~.\-]/g,

        // Match characters not in the ureserved or reserved lists.
        UR: /[^\w.~:\/\?#\[\]@!\$&'()*+,;=\-]/g,

        // Match invalid URI characters, and "%" characters not part of a
        // percent-encoded character. Percent-encoding detection can produce
        // false positives.
        L: /[\x01-\x1A]|[ "'<>\\\^`{|}]|%(?![a-fA-F0-9]{2})/g
    };

// Escape additional URI characters per RFC 3986: !, ', (, ), *
function _encodeURIComponent(str) {
    return encodeURIComponent(str)
        .replace(/!/g, '%21')
        .replace(/'/g, '%27')
        .replace(/\(/g, '%28')
        .replace(/\)/g, '%29')
        .replace(/\*/g, '%2A');
}

/**
URL-encode portions of a value that match a regular expression. Optionally
avoid double-encoding of already URL-encoded values. Non-string values are
coerced to string before encoding.

@method encode
@param {Mixed} value The value to encode.
@param {RegExp} re Regular expression used for matching.
@param {Boolean} [decode] Whether or not to decode the string first, to
    prevent double-encoding. Default is false.
@return {String} The URI encoded string.
**/
function encode(value, re, decode) {
    var string = '' + value,
        m;

    if (decode && /\%/.test(string)) {
        try {
            string = decodeURIComponent(string);
        } catch (e) {}
    }

    return (function next(start) {
        if (m = re.exec(string)) {
            return string.slice(start, m.index) + _encodeURIComponent(m[0]) +
                next(m.index + 1);
        }

        return string.substring(start);
    }(0));
}

Temple.Encoder = {
    varname   : encodeURIComponent,
    literal   : function (value) { return encode(value, re.L);        },
    unreserved: function (value) { return encode(value, re.U,  true); },
    reserved  : function (value) { return encode(value, re.UR, true); }
};

// Shorthand aliases.
Temple.Encoder.L      = Temple.Encoder.literal;
Temple.Encoder.U      = Temple.Encoder.unreserved;
Temple.Encoder['U+R'] = Temple.Encoder.reserved;

Temple.Array = {
    each: Aproto.forEach ? function (array, fn, thisObj) {
        array.forEach(fn, thisObj);
    } : function (array, fn, thisObj) {
        var i, len;

        for (i = 0, len = (array && array.length) || 0; i < len; ++i) {
            if (array[i] !== undefined) {
                fn.call(thisObj, array[i], i, array);
            }
        }
    },

    filter: Aproto.filter ? function (array, fn, thisObj) {
        return array.filter(fn, thisObj);
    } : function (array, fn, thisObj) {
        var results = [];

        Temple.Array.each(array, function (item, index) {
            if (fn.call(thisObj, item, index, array)) {
                results.push(item);
            }
        });

        return results;
    },

    isArray: Array.isArray ? Array.isArray : function (o) {
        return Object.prototype.toString.call(o) === '[object Array]';
    },

    map: Aproto.map ? function (array, fn, thisObj) {
        return array.map(fn, thisObj);
    } : function (array, fn, thisObj) {
        var results = array.concat();

        Temple.Array.each(array, function (item, index) {
            results = fn.call(thisObj, item, index, array);
        }, thisObj);

        return results;
    }
};

Temple.Object = {
    each: function (o, fn, thisObj) {
        var key;

        for (key in o) {
            if (o.hasOwnProperty(key)) {
                fn.call(thisObj, o[key], key, o);
            }
        }
    },

    some: function (o, fn, thisObj) {
        var key;

        for (key in o) {
            if (o.hasOwnProperty(key) && fn.call(thisObj, o[key], key, o)) {
                return true;
            }
        }

        return false;
    }
};

}());

// </browser>

module.exports = Temple;
<|endoftext|>


<commit_before>// *************************************************************************
// Copyright (c) 2014-2017, SUSE LLC
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of SUSE LLC nor the names of its contributors may be
// used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// *************************************************************************
//
// app/lock-lib.js
//
"use strict";

define ([
    'jquery',
    'ajax',
    'app/caches',
    'current-user',
    'datetime',
    'lib',
    'stack',
], function (
    $,
    ajax,
    appCaches,
    currentUser,
    dt,
    coreLib,
    stack,
) {

    var
        createLockCheckMandatoryProps = function (obj) {
            return true;
        },

        createLockSave = function (obj) {
            var caller = stack.getTarget().name,
                cu = currentUser('obj'),
                intvl,
                rest,
                sc = function (st) {
                         stack.unwindToTarget(
                             'createLock',
                             emptyObj,
                             {
                                 "resultLine": "Lock " + st.payload.lid + " created",
                                 "inputId": "iNyear",
                             }
                         );
                     };
            console.log("Entering createLockSave() from caller " + caller + " with obj", obj);
            if (! createLockCheckMandatoryProps(obj)) {
                return null;
            }
            intvl = scrapeForm();
            if (! intvl) {
                return null;
            }
            rest = {
                "method": 'POST',
                "path": 'lock/new',
                "body": {
                    "eid": cu.eid,
                    "intvl": intvl,
                    "remark": null,
                },
            }
            ajax(rest, sc);
        },

        deleteLock = function (obj) {
            var rest = {
                    "method": "DELETE",
                    "path": "lock/lid/" + obj.lid,
                },
                sc = function (st) {
                    if (st.code === 'DOCHAZKA_CUD_OK') {
                        stack.unwindToTarget('viewLocksAction');
                    } else {
                        coreLib.displayError(st.text);
                    }
                };
            console.log("Entering deleteLock() with obj", obj);
            ajax(rest, sc);
        },

        emptyObj = {
            "iNyear": "",
            "iNmonth": "",
        },

        scrapeForm = function () {
            var begin,
                end,
                month = $('input[id="iNmonth"]').val(),
                year = $('input[id="iNyear"]').val();
            if (! coreLib.isInteger(month)) {
                month = dt.monthToInt(month);
            }
            if (month < 1 || month > 12) {
                coreLib.displayError("Invalid month");
                return null;
            }
            begin = String(year) + "-" + String(month) + "-1";
            end = String(year) + "-" + String(month) + "-" + String(dt.daysInMonth(year, month));
            return "[ " + String(begin) + " 00:00, " + String(end) + " 24:00 )";
        },

        viewLocksAction = function () {
            // scrape year and month from form, generate tsrange
            // call GET lock/eid/:eid/:tsrange
            // viewLocksDrowselect on the resulting object
            var begin,
                cu = currentUser('obj'),
                end,
                intvl,
                i,
                obj,
                opts,
                year,
                rest,
                sc = function (st) {
                    var ld;
                    if (st.code === 'DISPATCH_RECORDS_FOUND' ) {
                        opts = { "resultLine": st.count + " locks found" };
                        opts['xtarget'] = 'viewLocksPrep'; // so we don't land in viewLocksAction
                        stack.push('viewLocksDrowselect', {
                            'pos': 0,
                            'set': st.payload
                        }, opts);
                    } else if (st.code === 'DISPATCH_NO_RECORDS_FOUND' ) {
                        coreLib.displayError(st.code + ": " + st.text);
                    } else {
                        coreLib.displayError(st.code + ": " + st.text);
                    }
                },
                fc = function (st) {
                    stack.pop(undefined, {"resultLine": st.payload.message});
                };
            // scrape year from form
            year = $('input[id="iNyear"]').val();
            begin = String(year) + "-1-1";
            end = String(year) + "-12-31";
            intvl = "[ " + String(begin) + " 00:00, " + String(end) + " 24:00 )";
            rest = {
                "method": 'GET',
                "path": 'lock/eid/' + cu.eid + "/" + intvl,
            };
            ajax(rest, sc, fc);
        }
        ;

    // here is where we define methods implementing the various
    // interval-related actions (see daction-start.js)
    return {
        createLockSave: createLockSave,
        deleteLock: deleteLock,
        viewLocksAction: viewLocksAction,
    };

});
<commit_msg>js: viewLocksAction: implement caching of year input<commit_after>// *************************************************************************
// Copyright (c) 2014-2017, SUSE LLC
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of SUSE LLC nor the names of its contributors may be
// used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// *************************************************************************
//
// app/lock-lib.js
//
"use strict";

define ([
    'jquery',
    'ajax',
    'app/caches',
    'current-user',
    'datetime',
    'lib',
    'stack',
], function (
    $,
    ajax,
    appCaches,
    currentUser,
    dt,
    coreLib,
    stack,
) {

    var
        createLockCheckMandatoryProps = function (obj) {
            return true;
        },

        createLockSave = function (obj) {
            var caller = stack.getTarget().name,
                cu = currentUser('obj'),
                intvl,
                rest,
                sc = function (st) {
                         stack.unwindToTarget(
                             'createLock',
                             emptyObj,
                             {
                                 "resultLine": "Lock " + st.payload.lid + " created",
                                 "inputId": "iNyear",
                             }
                         );
                     };
            console.log("Entering createLockSave() from caller " + caller + " with obj", obj);
            if (! createLockCheckMandatoryProps(obj)) {
                return null;
            }
            intvl = scrapeForm();
            if (! intvl) {
                return null;
            }
            rest = {
                "method": 'POST',
                "path": 'lock/new',
                "body": {
                    "eid": cu.eid,
                    "intvl": intvl,
                    "remark": null,
                },
            }
            ajax(rest, sc);
        },

        deleteLock = function (obj) {
            var rest = {
                    "method": "DELETE",
                    "path": "lock/lid/" + obj.lid,
                },
                sc = function (st) {
                    if (st.code === 'DOCHAZKA_CUD_OK') {
                        stack.unwindToTarget('viewLocksAction');
                    } else {
                        coreLib.displayError(st.text);
                    }
                };
            console.log("Entering deleteLock() with obj", obj);
            ajax(rest, sc);
        },

        emptyObj = {
            "iNyear": "",
            "iNmonth": "",
        },

        scrapeForm = function () {
            var begin,
                end,
                month = $('input[id="iNmonth"]').val(),
                year = $('input[id="iNyear"]').val();
            if (! coreLib.isInteger(month)) {
                month = dt.monthToInt(month);
            }
            if (month < 1 || month > 12) {
                coreLib.displayError("Invalid month");
                return null;
            }
            begin = String(year) + "-" + String(month) + "-1";
            end = String(year) + "-" + String(month) + "-" + String(dt.daysInMonth(year, month));
            return "[ " + String(begin) + " 00:00, " + String(end) + " 24:00 )";
        },

        viewLocksActionCache = function () {
            var i, frm = [];
            frm[0] = coreLib.nullify($('input[id="iNyear"]').val());
            for (i = 0; i < 1; i += 1) {
                if (frm[i]) {
                    viewLocksCache[i] = frm[i];
                } else {
                    frm[i] = viewLocksCache[i];
                }
            }
            // console.log("viewLocksActionCache() returning", frm);
            return frm;
        },
        viewLocksAction = function () {
            // scrape year and month from form, generate tsrange
            // call GET lock/eid/:eid/:tsrange
            // viewLocksDrowselect on the resulting object
            var begin,
                cu = currentUser('obj'),
                end,
                intvl,
                i,
                obj,
                opts,
                year,
                rest,
                sc = function (st) {
                    var ld;
                    if (st.code === 'DISPATCH_RECORDS_FOUND' ) {
                        opts = { "resultLine": st.count + " locks found" };
                        opts['xtarget'] = 'viewLocksPrep'; // so we don't land in viewLocksAction
                        stack.push('viewLocksDrowselect', {
                            'pos': 0,
                            'set': st.payload
                        }, opts);
                    } else if (st.code === 'DISPATCH_NO_RECORDS_FOUND' ) {
                        coreLib.displayError(st.code + ": " + st.text);
                    } else {
                        coreLib.displayError(st.code + ": " + st.text);
                    }
                },
                fc = function (st) {
                    stack.pop(undefined, {"resultLine": st.payload.message});
                };
            // scrape year from form
            [year] = viewLocksActionCache();
            begin = String(year) + "-1-1";
            end = String(year) + "-12-31";
            intvl = "[ " + String(begin) + " 00:00, " + String(end) + " 24:00 )";
            rest = {
                "method": 'GET',
                "path": 'lock/eid/' + cu.eid + "/" + intvl,
            };
            ajax(rest, sc, fc);
        },
        viewLocksCache = []
        ;

    // here is where we define methods implementing the various
    // interval-related actions (see daction-start.js)
    return {
        createLockSave: createLockSave,
        deleteLock: deleteLock,
        viewLocksAction: viewLocksAction,
    };

});
<|endoftext|>


<commit_before>/*
 Copyright (c) 2011, 2015, Jessica González
 @license http://www.mozilla.org/MPL/ MPL 2.0
*/
$(function() {

	var previewNode = document.querySelector('#template');
	previewNode.id = '';
	var previewTemplate = previewNode.parentNode.innerHTML;
	previewNode.parentNode.removeChild(previewNode);

	var dOptions = {
		url: smf_scripturl + '?action=breezeajax;sa=cover;rf=profile;u='+ smf_member_id +';area=breezecover;js=1;'+ smf_session_var +'='+ smf_session_id,
		maxFilesize: 1,
		uploadMultiple:false,
		maxFiles:1,
		acceptedFiles: '',
		thumbnailWidth: 100,
		thumbnailHeight: null,
		parallelUploads: 3,
		previewTemplate: previewTemplate,
		autoQueue: false,
		previewsContainer: '#cu-previews',
		clickable: '.fileinput-button',
		dictDefaultMessage: 'Drop files here to upload',
		paramName: 'files',
		accept: function(file, done) {

			file.acceptDimensions = done;
			file.rejectDimensions = function(eText) { done(eText); };
		}
	};

	$.extend(true, dOptions, dzOptions);

	var myDropzone = new Dropzone('div#coverUpload', dOptions);

	myDropzone.on('thumbnail', function(file) {

		if (file.width > myDropzone.options.maxFileWidth) {
			file.rejectDimensions(myDropzone.options.maxWidthMessage);
		}
		else if (file.height > myDropzone.options.maxFileHeight){
			file.rejectDimensions(myDropzone.options.maxHeightMessage);
		}
		else {
			file.acceptDimensions();
		}
	});

	myDropzone.on('addedfile', function(file) {

		// Hookup the start button
		file.previewElement.querySelector('.start').onclick = function() { myDropzone.enqueueFile(file); };

		// Show the main stuff!
		file.previewElement.setAttribute("class", "descbox");
	});

	// Update the total progress bar
	myDropzone.on('totaluploadprogress', function(progress) {
		document.querySelector('#total-progress .progress-bar').style.width = progress + '%';
	});

	myDropzone.on('error', function(file, errorMessage, xhr) {
		// Remove the "start" button.
		file.previewElement.querySelector('.start').remove();

		// Set a nice css class to make it more obvious theres an error.
		file.previewElement.removeAttribute("class", "descbox");
		file.previewElement.setAttribute("class", "errorbox");
	});

	myDropzone.on('success', function(file, responseText, e) {
			var _thisElement = $(file.previewElement);

		// The request was complete but the server returned an error.
		if (typeof(responseText.error) != "undefined"){

			_thisElement.toggleClass("errorbox");

			// Show the server error.
			_thisElement.find('p.error').append(responseText.error);

			// Remove the "start" button.
			_thisElement.find('.start').remove();
		}

		// If there wasn't any error, change the current cover.
		if (responseText.type == 'info'){
			_thisElement.toggleClass("infobox");
		}
	});

	myDropzone.on('complete', function(file) {

		// Doesn't matter what the result was, remove the ajax indicator.
		ajax_indicator(false);
	});

	myDropzone.on('sending', function(file) {

		// Hey! we are actually doing something!
		ajax_indicator(true);

		// Show the total progress bar when upload starts
		document.querySelector('#total-progress').style.opacity = '1';
		// And disable the start button
		file.previewElement.querySelector('.start').setAttribute('disabled', 'disabled');
	});

	// Hide the total progress bar when nothing's uploading anymore
	myDropzone.on('queuecomplete', function(progress) {
		document.querySelector('#total-progress').style.opacity = '0';
	});
});<commit_msg>Unneeded js<commit_after>/*
 Copyright (c) 2011, 2015, Jessica González
 @license http://www.mozilla.org/MPL/ MPL 2.0
*/
$(function() {

	var previewNode = document.querySelector('#template');
	previewNode.id = '';
	var previewTemplate = previewNode.parentNode.innerHTML;
	previewNode.parentNode.removeChild(previewNode);

	var dOptions = {
		url: smf_scripturl + '?action=breezeajax;sa=cover;rf=profile;u='+ smf_member_id +';area=breezecover;js=1;'+ smf_session_var +'='+ smf_session_id,
		maxFilesize: 1,
		uploadMultiple:false,
		maxFiles:1,
		acceptedFiles: '',
		thumbnailWidth: 100,
		thumbnailHeight: null,
		parallelUploads: 3,
		previewTemplate: previewTemplate,
		autoQueue: false,
		previewsContainer: '#cu-previews',
		clickable: '.fileinput-button',
		dictDefaultMessage: 'Drop files here to upload',
		paramName: 'files',
		accept: function(file, done) {

			file.acceptDimensions = done;
			file.rejectDimensions = function(eText) { done(eText); };
		}
	};

	$.extend(true, dOptions, dzOptions);

	var myDropzone = new Dropzone('div#coverUpload', dOptions);

	myDropzone.on('thumbnail', function(file) {

		if (file.width > myDropzone.options.maxFileWidth) {
			file.rejectDimensions(myDropzone.options.maxWidthMessage);
		}
		else if (file.height > myDropzone.options.maxFileHeight){
			file.rejectDimensions(myDropzone.options.maxHeightMessage);
		}
		else {
			file.acceptDimensions();
		}
	});

	myDropzone.on('addedfile', function(file) {

		_thisElement = $(file.previewElement);

		// Hookup the start button.
		_thisElement.find('.start').on( 'click', function() {
			myDropzone.enqueueFile(file);
		});

		// Show the main stuff!
		_thisElement.addClass('descbox');
	});

	myDropzone.on('error', function(file, errorMessage, xhr) {

		_thisElement = $(file.previewElement);

		// Remove the "start" button.
		_thisElement.find('.start').remove();

		// Set a nice css class to make it more obvious theres an error.
		_thisElement.removeClass('infobox').addClass('errorbox');
	});

	myDropzone.on('success', function(file, responseText, e) {

		_thisElement = $(file.previewElement);

		// The request was complete but the server returned an error.
		if (typeof(responseText.error) != "undefined"){

			_thisElement.removeClass('descbox').addClass('errorbox');

			// Show the server error.
			_thisElement.find('p.error').append(responseText.error);

			// Remove the "start" button.
			_thisElement.find('.start').remove();
		}

		// If there wasn't any error, change the current cover.
		if (responseText.type == 'info'){
			_thisElement.removeClass('descboxbox').addClass('infobox');
		}
	});

	myDropzone.on('complete', function(file) {

		// Doesn't matter what the result was, remove the ajax indicator.
		ajax_indicator(false);
	});

	myDropzone.on('sending', function(file) {

		_thisElement = $(file.previewElement);

		// Hey! we are actually doing something!
		ajax_indicator(true);

		// And disable the start button
		_thisElement.find('.start').prop('disabled', true);
	});
});<|endoftext|>


<commit_before>/*
 Copyright 2015 Google Inc. All rights reserved.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

(function() {
    var module = angular.module('timesketch.explore.histogram.directive', []);

    module.directive('tsHistogram', function ($window, timesketchApi) {
        /**
         * Histogram chart for number of events.
         * @param sketchId - Sketch ID.
         * @param filter - Filter object.
         * @param query - Query string.
         * @param meta - Events metadata object.
         * @param showCharts - Boolean indicating if chars should be visible.
         */
        return {
            restrict: 'E',
            scope: {
                sketchId: '=',
                filter: '=',
                query: '=',
                meta: '=',
                showCharts: '='
            },
            require: '^tsSearch',
            link: function(scope, element, attrs, ctrl) {
                scope.$watchGroup(['meta', 'showCharts'], function (newval, oldval) {
                    if(scope.showCharts) {
                        timesketchApi.aggregation(scope.sketchId, scope.query, scope.filter, 'histogram')
                            .success(function(data) {
                                scope.render_histogram(data['objects'])
                            });
                    }
                }, true);

                // Handle window resize, and redraw the chart automatically.
                $window.onresize = function() {
                    scope.$apply();
                };
                scope.$watch(function() {
                    return angular.element($window)[0].innerWidth;
                }, function() {
                    if(scope.meta) {
                        timesketchApi.aggregation(scope.sketchId, scope.query, scope.filter, 'histogram')
                            .success(function(data) {
                                scope.render_histogram(data['objects'])
                            });
                    }
                });

                // Render the chart svg with D3.js
                scope.render_histogram = function(data) {
                    d3.select('.histogram').remove();

                    var margin = { top: 50, right: 75, bottom: 100, left: 40 },
                        svgWidth = d3.select(d3.select(element[0].parentElement.parentElement.parentElement.offsetParent.offsetWidth)) - margin.left - margin.right,
                        svgHeight = 500 - margin.top - margin.bottom;

                    var	parseDate = d3.time.format("%Y-%m-%d").parse;

                    var x = d3.scale.ordinal().rangeRoundBands([0, svgWidth], .05);
                    var y = d3.scale.linear().range([svgHeight, 0]);

                    var xAxis = d3.svg.axis()
                        .scale(x)
                        .orient("bottom")
                        .tickFormat(d3.time.format("%Y-%m-%d"));

                    var svg = d3.select(element[0]).append("svg")
                        .attr("width", svgWidth + margin.left + margin.right)
                        .attr("height", svgHeight + margin.top + margin.bottom)
                        .classed("histogram", true)
                        .append("g")
                        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

                    data.forEach(function(d) {
                        d.key_as_string = parseDate(d.key_as_string);
                        d.doc_count = +d.doc_count;
                    });

                    x.domain(data.map(function(d) { return d.key_as_string; }));
                    y.domain([0, d3.max(data, function(d) { return d.doc_count; })]);

                    svg.append("g")
                        .attr("class", "x axis")
                        .attr("transform", "translate(0," + svgHeight + ")")
                        .call(xAxis)
                        .selectAll("text")
                        .style("text-anchor", "end")
                        .attr("dx", "-.8em")
                        .attr("dy", "-.55em")
                        .attr("transform", "rotate(-90)" );

                    svg.selectAll("bar")
                        .data(data)
                        .enter().append("rect")
                        .style("fill", "steelblue")
                        .attr("x", function(d) { return x(d.key_as_string); })
                        .attr("width", x.rangeBand())
                        .attr("y", function(d) { return y(d.doc_count); })
                        .attr("height", function(d) { return svgHeight - y(d.doc_count); });
                };
            }
        }
    });
})();
<commit_msg>Labels<commit_after>/*
 Copyright 2015 Google Inc. All rights reserved.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

(function() {
    var module = angular.module('timesketch.explore.histogram.directive', []);

    module.directive('tsHistogram', function ($window, timesketchApi) {
        /**
         * Histogram chart for number of events.
         * @param sketchId - Sketch ID.
         * @param filter - Filter object.
         * @param query - Query string.
         * @param meta - Events metadata object.
         * @param showCharts - Boolean indicating if chars should be visible.
         */
        return {
            restrict: 'E',
            scope: {
                sketchId: '=',
                filter: '=',
                query: '=',
                meta: '=',
                showCharts: '='
            },
            require: '^tsSearch',
            link: function(scope, element, attrs, ctrl) {
                scope.$watchGroup(['meta', 'showCharts'], function (newval, oldval) {
                    if(scope.showCharts) {
                        timesketchApi.aggregation(scope.sketchId, scope.query, scope.filter, 'histogram')
                            .success(function(data) {
                                scope.render_histogram(data['objects'])
                            });
                    }
                }, true);

                // Handle window resize, and redraw the chart automatically.
                $window.onresize = function() {
                    scope.$apply();
                };
                scope.$watch(function() {
                    return angular.element($window)[0].innerWidth;
                }, function() {
                    if(scope.meta) {
                        timesketchApi.aggregation(scope.sketchId, scope.query, scope.filter, 'histogram')
                            .success(function(data) {
                                scope.render_histogram(data['objects'])
                            });
                    }
                });

                // Render the chart svg with D3.js
                scope.render_histogram = function(data) {
                    d3.select('.histogram').remove();

                    var margin = { top: 50, right: 75, bottom: 100, left: 40 },
                        svgWidth = d3.select(d3.select(element[0].parentElement.parentElement.parentElement.offsetParent.offsetWidth)) - margin.left - margin.right,
                        svgHeight = 500 - margin.top - margin.bottom;

                    var	parseDate = d3.time.format("%Y-%m-%d").parse;

                    var x = d3.scale.ordinal().rangeRoundBands([0, svgWidth], .05);
                    var y = d3.scale.linear().range([svgHeight, 0]);

                    var xAxis = d3.svg.axis()
                        .scale(x)
                        .orient("bottom")
                        .tickFormat(d3.time.format("%Y-%m-%d"));

                    var svg = d3.select(element[0]).append("svg")
                        .attr("width", svgWidth + margin.left + margin.right)
                        .attr("height", svgHeight + margin.top + margin.bottom)
                        .classed("histogram", true)
                        .append("g")
                        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

                    data.forEach(function(d) {
                        d.key_as_string = parseDate(d.key_as_string);
                        d.doc_count = +d.doc_count;
                    });

                    x.domain(data.map(function(d) { return d.key_as_string; }));
                    y.domain([0, d3.max(data, function(d) { return d.doc_count; })]);

                    svg.append("g")
                        .attr("class", "x axis")
                        .attr("transform", "translate(0," + svgHeight + ")")
                        .call(xAxis)
                        .selectAll("text")
                        .style("text-anchor", "end")
                        .attr("dx", "-.8em")
                        .attr("dy", "-.55em")
                        .attr("transform", "rotate(-90)" );

                    svg.selectAll("bar")
                        .data(data)
                        .enter()
                        .append("rect")
                        .style("fill", "#428bca")
                        .attr("x", function(d) { return x(d.key_as_string); })
                        .attr("width", x.rangeBand())
                        .attr("y", function(d) { return y(d.doc_count); })
                        .attr("height", function(d) { return svgHeight - y(d.doc_count); });

                    svg.selectAll("bar")
                        .data(data)
                        .enter()
                        .append("text")
                        .text(function(d) {
                            return d.doc_count;
                        })
                        .attr("text-anchor", "middle")
                        .attr("x", function(d) {
                            return x(d.key_as_string) + (x.rangeBand() / 2);
                        })
                        .attr("y", function(d) { return y(d.doc_count) - 10; })
                        .attr("font-family", "sans-serif")
                        .attr("font-size", "14px")
                        .attr("fill", "black");

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


<commit_before>// @flow
import _ from 'lodash'
import {
  KbfsCommonFSErrorType,
  KbfsCommonFSNotificationType,
  KbfsCommonFSStatusCode,
} from '../constants/types/flow-types'
import path from 'path'
import {parseFolderNameToUsers} from './kbfs'
import type {FSNotification} from '../constants/types/flow-types'

type DecodedKBFSError = {
  'title': string,
  'body': string,
}

function usernamesForNotification(notification: FSNotification) {
  return parseFolderNameToUsers(null, notification.filename.split(path.sep)[3] || notification.filename).map(
    i => i.username
  )
}

function tlfForNotification(notification: FSNotification): string {
  // The notification.filename is canonical platform independent path.
  // To get the TLF we can look at the first 3 directories.
  // /keybase/private/gabrielh/foo.txt => /keybase/private/gabrielh
  return notification.filename.split(path.sep).slice(0, 4).join(path.sep)
}

export function decodeKBFSError(user: string, notification: FSNotification): DecodedKBFSError {
  console.log('Notification (kbfs error):', notification)
  const tlf = tlfForNotification(notification)
  switch (notification.errorType) {
    case KbfsCommonFSErrorType.accessDenied:
      let prefix = user ? `${user} does` : 'You do'
      return {
        title: 'Keybase: Access denied',
        body: `${prefix} not have ${notification.params.mode} access to ${notification.filename}`,
      }

    case KbfsCommonFSErrorType.userNotFound:
      return {
        title: 'Keybase: User not found',
        body: `${notification.params.username} is not a Keybase user`,
      }

    case KbfsCommonFSErrorType.revokedDataDetected:
      return {
        title: 'Keybase: Possibly revoked data detected',
        body: `${tlf} was modified by a revoked or bad device. Use 'keybase log send' to file an issue with the Keybase admins.`,
      }

    case KbfsCommonFSErrorType.notLoggedIn:
      return {
        title: `Keybase: Permission denied in ${tlf}`,
        body: "You are not logged into Keybase. Try 'keybase login'.",
      }

    case KbfsCommonFSErrorType.timeout:
      return {
        title: `Keybase: ${_.capitalize(notification.params.mode)} timeout in ${tlf}`,
        body: `The ${notification.params.mode} operation took too long and failed. Please run 'keybase log send' so our admins can review.`,
      }

    case KbfsCommonFSErrorType.rekeyNeeded:
      return notification.params.rekeyself === 'true'
        ? {
            title: 'Keybase: Files need to be rekeyed',
            body: `Please open one of your other computers to unlock ${tlf}`,
          }
        : {
            title: 'Keybase: Friends needed',
            body: `Please ask another member of ${tlf} to open Keybase on one of their computers to unlock it for you.`,
          }

    case KbfsCommonFSErrorType.overQuota:
      const usageBytes = parseInt(notification.params.usageBytes, 10)
      const limitBytes = parseInt(notification.params.limitBytes, 10)
      const usedGB = (usageBytes / 1e9).toFixed(1)
      const usedPercent = Math.round(100 * usageBytes / limitBytes)
      return {
        title: 'Keybase: Out of space',
        body: `Action needed! You are using ${usedGB}GB (${usedPercent}%) of your quota. Please delete some data.`,
      }

    default:
      return {
        title: 'Keybase: KBFS error',
        body: `${notification.status}`,
      }
  }

  // This code came from the kbfs team but this isn't plumbed through the protocol. Leaving this for now
  // if (notification.errorType === KbfsCommonFSErrorType.notImplemented) {
  // if (notification.feature === '2gbFileLimit') {
  // return ({
  // title: 'Keybase: Not yet implemented',
  // body: `You just tried to write a file larger than 2GB in ${tlf}. This limitation will be removed soon.`
  // })
  // } else if (notification.feature === '512kbDirLimit') {
  // return ({
  // title: 'Keybase: Not yet implemented',
  // body: `You just tried to write too many files into ${tlf}. This limitation will be removed soon.`
  // })
  // } else {
  // return ({
  // title: 'Keybase: Not yet implemented',
  // body: `You just hit a ${notification.feature} limitation in KBFS. It will be fixed soon.`
  // })
  // }
  // } else {
  // return ({
  // title: 'Keybase: KBFS error',
  // body: `${notification.status}`
  // })
  // }
}

export function kbfsNotification(notification: FSNotification, notify: any, getState: any) {
  const action = {
    [KbfsCommonFSNotificationType.encrypting]: 'Encrypting and uploading',
    [KbfsCommonFSNotificationType.decrypting]: 'Decrypting',
    [KbfsCommonFSNotificationType.signing]: 'Signing and uploading',
    [KbfsCommonFSNotificationType.verifying]: 'Verifying and downloading',
    [KbfsCommonFSNotificationType.rekeying]: 'Rekeying',
  }[notification.notificationType]

  if (action === undefined) {
    // Ignore notification types we don't care about.
    return
  }

  // KBFS fires a notification when it changes state between connected
  // and disconnected (to the mdserver).  For now we just log it.
  if (notification.notificationType === KbfsCommonFSNotificationType.connection) {
    const state = notification.statusCode === KbfsCommonFSStatusCode.start ? 'connected' : 'disconnected'
    console.log(`KBFS is ${state}`)
    return
  }

  const usernames = usernamesForNotification(notification).join(' & ')

  let title = `KBFS: ${action}`
  let body = `Chat or files with ${usernames} ${notification.status}`
  let user = getState().config.username
  let rateLimitKey

  const isError = notification.statusCode === KbfsCommonFSStatusCode.error
  // Don't show starting or finished, but do show error.
  if (isError) {
    if (notification.errorType === KbfsCommonFSErrorType.exdevNotSupported) {
      // Ignored for now.
      // TODO: implement the special popup window (DESKTOP-3637)
      return
    }

    ;({title, body} = decodeKBFSError(user, notification))
    rateLimitKey = body // show unique errors
  } else {
    switch (action) {
      case 'Rekeying': // limit all rekeys, no matter the tlf
        rateLimitKey = 'rekey'
        break
      default:
        rateLimitKey = usernames // by tlf
    }
  }

  notify(title, {body}, 10, rateLimitKey)
}
<commit_msg>Display kbfs error when local journal is full<commit_after>// @flow
import _ from 'lodash'
import {
  KbfsCommonFSErrorType,
  KbfsCommonFSNotificationType,
  KbfsCommonFSStatusCode,
} from '../constants/types/flow-types'
import path from 'path'
import {parseFolderNameToUsers} from './kbfs'
import type {FSNotification} from '../constants/types/flow-types'

type DecodedKBFSError = {
  'title': string,
  'body': string,
}

function usernamesForNotification(notification: FSNotification) {
  return parseFolderNameToUsers(null, notification.filename.split(path.sep)[3] || notification.filename).map(
    i => i.username
  )
}

function tlfForNotification(notification: FSNotification): string {
  // The notification.filename is canonical platform independent path.
  // To get the TLF we can look at the first 3 directories.
  // /keybase/private/gabrielh/foo.txt => /keybase/private/gabrielh
  return notification.filename.split(path.sep).slice(0, 4).join(path.sep)
}

export function decodeKBFSError(user: string, notification: FSNotification): DecodedKBFSError {
  console.log('Notification (kbfs error):', notification)
  const tlf = tlfForNotification(notification)
  switch (notification.errorType) {
    case KbfsCommonFSErrorType.accessDenied:
      let prefix = user ? `${user} does` : 'You do'
      return {
        title: 'Keybase: Access denied',
        body: `${prefix} not have ${notification.params.mode} access to ${notification.filename}`,
      }

    case KbfsCommonFSErrorType.userNotFound:
      return {
        title: 'Keybase: User not found',
        body: `${notification.params.username} is not a Keybase user`,
      }

    case KbfsCommonFSErrorType.revokedDataDetected:
      return {
        title: 'Keybase: Possibly revoked data detected',
        body: `${tlf} was modified by a revoked or bad device. Use 'keybase log send' to file an issue with the Keybase admins.`,
      }

    case KbfsCommonFSErrorType.notLoggedIn:
      return {
        title: `Keybase: Permission denied in ${tlf}`,
        body: "You are not logged into Keybase. Try 'keybase login'.",
      }

    case KbfsCommonFSErrorType.timeout:
      return {
        title: `Keybase: ${_.capitalize(notification.params.mode)} timeout in ${tlf}`,
        body: `The ${notification.params.mode} operation took too long and failed. Please run 'keybase log send' so our admins can review.`,
      }

    case KbfsCommonFSErrorType.rekeyNeeded:
      return notification.params.rekeyself === 'true'
        ? {
            title: 'Keybase: Files need to be rekeyed',
            body: `Please open one of your other computers to unlock ${tlf}`,
          }
        : {
            title: 'Keybase: Friends needed',
            body: `Please ask another member of ${tlf} to open Keybase on one of their computers to unlock it for you.`,
          }
    // Aggregate these cases together since they both use the usage/limit calc
    case KbfsCommonFSErrorType.overQuota:
    case KbfsCommonFSErrorType.diskLimitReached:
      const usageBytes = parseInt(notification.params.usageBytes, 10)
      const limitBytes = parseInt(notification.params.limitBytes, 10)
      const usedGB = (usageBytes / 1e9).toFixed(1)
      const usedPercent = Math.round(100 * usageBytes / limitBytes)
      if (notification.errorType === KbfsCommonFSErrorType.overQuota) {
        return {
          title: 'Keybase: Out of space',
          body: `Action needed! You are using ${usedGB}GB (${usedPercent}%) of your quota. Please delete some data.`,
        }
      } else {
        // diskLimitReached
        if (usageBytes >= 0.99 * limitBytes) {
          return {
            title: 'Keybase: Out of temporary space',
            body: `Keybase is using ${usedPercent}% of its temporary write space (${usedGB}GB), and writes will fail until the data syncs to the remote server.`,
          }
        } else {
          return {
            title: 'Keybase: Out of temporary space',
            body: 'Keybase is using too many file system resources temporarily, and writes will fail until the data syncs to the remote server.',
          }
        }
      }

    default:
      return {
        title: 'Keybase: KBFS error',
        body: `${notification.status}`,
      }
  }

  // This code came from the kbfs team but this isn't plumbed through the protocol. Leaving this for now
  // if (notification.errorType === KbfsCommonFSErrorType.notImplemented) {
  // if (notification.feature === '2gbFileLimit') {
  // return ({
  // title: 'Keybase: Not yet implemented',
  // body: `You just tried to write a file larger than 2GB in ${tlf}. This limitation will be removed soon.`
  // })
  // } else if (notification.feature === '512kbDirLimit') {
  // return ({
  // title: 'Keybase: Not yet implemented',
  // body: `You just tried to write too many files into ${tlf}. This limitation will be removed soon.`
  // })
  // } else {
  // return ({
  // title: 'Keybase: Not yet implemented',
  // body: `You just hit a ${notification.feature} limitation in KBFS. It will be fixed soon.`
  // })
  // }
  // } else {
  // return ({
  // title: 'Keybase: KBFS error',
  // body: `${notification.status}`
  // })
  // }
}

export function kbfsNotification(notification: FSNotification, notify: any, getState: any) {
  const action = {
    [KbfsCommonFSNotificationType.encrypting]: 'Encrypting and uploading',
    [KbfsCommonFSNotificationType.decrypting]: 'Decrypting',
    [KbfsCommonFSNotificationType.signing]: 'Signing and uploading',
    [KbfsCommonFSNotificationType.verifying]: 'Verifying and downloading',
    [KbfsCommonFSNotificationType.rekeying]: 'Rekeying',
  }[notification.notificationType]

  if (action === undefined) {
    // Ignore notification types we don't care about.
    return
  }

  // KBFS fires a notification when it changes state between connected
  // and disconnected (to the mdserver).  For now we just log it.
  if (notification.notificationType === KbfsCommonFSNotificationType.connection) {
    const state = notification.statusCode === KbfsCommonFSStatusCode.start ? 'connected' : 'disconnected'
    console.log(`KBFS is ${state}`)
    return
  }

  const usernames = usernamesForNotification(notification).join(' & ')

  let title = `KBFS: ${action}`
  let body = `Chat or files with ${usernames} ${notification.status}`
  let user = getState().config.username
  let rateLimitKey

  const isError = notification.statusCode === KbfsCommonFSStatusCode.error
  // Don't show starting or finished, but do show error.
  if (isError) {
    if (notification.errorType === KbfsCommonFSErrorType.exdevNotSupported) {
      // Ignored for now.
      // TODO: implement the special popup window (DESKTOP-3637)
      return
    }

    ;({title, body} = decodeKBFSError(user, notification))
    rateLimitKey = body // show unique errors
  } else {
    switch (action) {
      case 'Rekeying': // limit all rekeys, no matter the tlf
        rateLimitKey = 'rekey'
        break
      default:
        rateLimitKey = usernames // by tlf
    }
  }

  notify(title, {body}, 10, rateLimitKey)
}
<|endoftext|>


<commit_before><commit_msg>Derp<commit_after><|endoftext|>


<commit_before>/**
 * jQuery Horizontal Navigation 1.0
 * https://github.com/sebnitu/horizontalNav
 *
 * By Sebastian Nitu - Copyright 2012 - All rights reserved
 * Author URL: http://sebnitu.com
 */
(function($) {

    $.fn.horizontalNav = function(options) {

        // Extend our default options with those provided.
        var opts = $.extend({}, $.fn.horizontalNav.defaults, options);

        return this.each(function () {

            // Save our object
            var $this = $(this);

            // Build element specific options
            // This lets me access options with this syntax: o.optionName
            var o = $.meta ? $.extend({}, opts, $this.data()) : opts;

            // Save the wrapper. The wrapper is the element that
            // we figure out what the full width should be
            if ($this.is('ul')) {
                var ul_wrap = $this.parent();
            } else {
                var ul_wrap = $this;
            }

            if ($('.clearHorizontalNav').length ) {
                ul_wrap.css({ 'zoom' : '1' });
            } else {
                ul_wrap.css({ 'zoom' : '1' }).append('
');
                // let's append a clearfixing element to the ul wrapper
                $('.clearHorizontalNav').css({
                'display' : 'block',
                'overflow' : 'hidden',
                'visibility' : 'hidden',
                'width' : 0,
                'height' : 0,
                'clear' : 'both'
                });
            }

            // Grab elements we'll need and add some default styles
            var ul = $this.is('ul') ? $this : ul_wrap.find('> ul'), // The unordered list element
                li = ul.find('> li'), // All list items
                li_last = li.last(), // Last list item
                li_count = li.size(), // The number of navigation elements
                li_a = li.find('> a'); // Remove padding from the links

            // If set to responsive, re-construct after every browser resize
            if ( o.responsive === true ) {
                // Only need to do this for IE7 and below
                // or if we set tableDisplay to false
                if ( (o.tableDisplay != true) || ($.browser.msie && parseInt($.browser.version, 10) <= 7) ) {
                    resizeTrigger( _construct, o.responsiveDelay );
                }
            }

            // Initiate the plugin
            _construct();

            // Returns the true inner width of an element
            // Essentially it's the inner width without padding.
            function trueInnerWidth(element) {
                return element.innerWidth() - (
                    parseInt(element.css('padding-left')) + parseInt(element.css('padding-right'))
                );
            }

            // Call funcion on browser resize
            function resizeTrigger(callback, delay) {
                // Delay before function is called
                delay = delay || 100;
                // Call function on resize
                var resizeTimer;
                $(window).resize(function() {
                    clearTimeout(resizeTimer);
                    resizeTimer = setTimeout(function() {
                        callback();
                    }, delay);
                });
            }

            // The heavy lifting of this plugin. This is where we
            // find and set the appropriate widths for list items
            function _construct() {

                if ( (o.tableDisplay != true) || ($.browser.msie && parseInt($.browser.version, 10) <= 7) ) {

                    // IE7 doesn't support the "display: table" method
                    // so we need to do it the hard way.

                    // Add some styles
                    ul.css({ 'float' : 'left' });
                    li.css({ 'float' : 'left', 'width' : 'auto' });
                    li_a.css({ 'padding-left' : 0, 'padding-right' : 0 });

                    // Grabbing widths and doing some math
                    var ul_width = trueInnerWidth(ul),
                        ul_width_outer = ul.outerWidth(true),
                        ul_width_extra = ul_width_outer - ul_width,

                        full_width = trueInnerWidth(ul_wrap),
                        extra_width = (full_width - ul_width_extra) - ul_width,
                        li_padding = Math.floor( extra_width / li_count );

                    // Cycle through the list items and give them widths
                    li.each(function(index) {
                        var li_width = trueInnerWidth( $(this) );
                        $(this).css({ 'width' : (li_width + li_padding) + 'px' });
                    });

                    // Get the leftover pixels after we set every itms width
                    var li_last_width = trueInnerWidth(li_last) + ( (full_width - ul_width_extra) - trueInnerWidth(ul) );
                    // I hate to do this but for some reason Firefox (v13.0) and IE are always
                    // one pixel off when rendering. So this is a quick fix for that.
                    if ($.browser.mozilla || $.browser.msie) {
                        li_last_width = li_last_width - 1;
                    }
                    // Add the leftovers to the last navigation item
                    li_last.css({ 'width' : li_last_width + 'px' });

                } else {
                    // Every modern browser supports the "display: table" method
                    // so this is the best way to do it for them.
                    ul.css({ 'display' : 'table', 'float' : 'none', 'width' : '100%' });
                    li.css({ 'display' : 'table-cell', 'float' : 'none' });
                }
            }

        }); // @end of return this.each()

    };

    $.fn.horizontalNav.defaults = {
        responsive : true,
        responsiveDelay : 100,
        tableDisplay : true
    };

})(jQuery);<commit_msg>Update jquery.horizontalNav.js<commit_after>/**
 * jQuery Horizontal Navigation 1.0
 * https://github.com/sebnitu/horizontalNav
 *
 * By Sebastian Nitu - Copyright 2012 - All rights reserved
 * Author URL: http://sebnitu.com
 */
(function($) {

    $.fn.horizontalNav = function(options) {

        // Extend our default options with those provided.
        var opts = $.extend({}, $.fn.horizontalNav.defaults, options);

        return this.each(function () {

            // Save our object
            var $this = $(this);

            // Build element specific options
            // This lets me access options with this syntax: o.optionName
            var o = $.meta ? $.extend({}, opts, $this.data()) : opts;

            // Save the wrapper. The wrapper is the element that
            // we figure out what the full width should be
            if ($this.is('ul')) {
                var ul_wrap = $this.parent();
            } else {
                var ul_wrap = $this;
            }

            // Grab elements we'll need and add some default styles
            var ul = $this.is('ul') ? $this : ul_wrap.find('> ul'), // The unordered list element
                li = ul.find('> li'), // All list items
                li_last = li.last(), // Last list item
                li_count = li.size(), // The number of navigation elements
                li_a = li.find('> a'); // Remove padding from the links

            if (o.minimumItems && o.minimumItems > li_count) {
                ul_wrap.addClass("horizontalNav-notprocessed");
                return false;
            }

            // If set to responsive, re-construct after every browser resize
            if ( o.responsive === true ) {
                // Only need to do this for IE7 and below
                // or if we set tableDisplay to false
                if ( (o.tableDisplay != true) || ($.browser.msie && parseInt($.browser.version, 10) <= 7) ) {
                    resizeTrigger( _construct, o.responsiveDelay );
                }
            }

            if ($('.clearHorizontalNav').length ) {
                ul_wrap.css({ 'zoom' : '1' });
            } else {
                ul_wrap.css({ 'zoom' : '1' }).append('
');
                // let's append a clearfixing element to the ul wrapper
                $('.clearHorizontalNav').css({
                'display' : 'block',
                'overflow' : 'hidden',
                'visibility' : 'hidden',
                'width' : 0,
                'height' : 0,
                'clear' : 'both'
                });
            }

            // Initiate the plugin
            _construct();

            // Returns the true inner width of an element
            // Essentially it's the inner width without padding.
            function trueInnerWidth(element) {
                return element.innerWidth() - (
                    parseInt(element.css('padding-left')) + parseInt(element.css('padding-right'))
                );
            }

            // Call funcion on browser resize
            function resizeTrigger(callback, delay) {
                // Delay before function is called
                delay = delay || 100;
                // Call function on resize
                var resizeTimer;
                $(window).resize(function() {
                    clearTimeout(resizeTimer);
                    resizeTimer = setTimeout(function() {
                        callback();
                    }, delay);
                });
            }

            // The heavy lifting of this plugin. This is where we
            // find and set the appropriate widths for list items
            function _construct() {

                if ( (o.tableDisplay != true) || ($.browser.msie && parseInt($.browser.version, 10) <= 7) ) {

                    // IE7 doesn't support the "display: table" method
                    // so we need to do it the hard way.

                    // Add some styles
                    ul.css({ 'float' : 'left' });
                    li.css({ 'float' : 'left', 'width' : 'auto' });
                    li_a.css({ 'padding-left' : 0, 'padding-right' : 0 });

                    // Grabbing widths and doing some math
                    var ul_width = trueInnerWidth(ul),
                        ul_width_outer = ul.outerWidth(true),
                        ul_width_extra = ul_width_outer - ul_width,

                        full_width = trueInnerWidth(ul_wrap),
                        extra_width = (full_width - ul_width_extra) - ul_width,
                        li_padding = Math.floor( extra_width / li_count );

                    // Cycle through the list items and give them widths
                    li.each(function(index) {
                        var li_width = trueInnerWidth( $(this) );
                        $(this).css({ 'width' : (li_width + li_padding) + 'px' });
                    });

                    // Get the leftover pixels after we set every itms width
                    var li_last_width = trueInnerWidth(li_last) + ( (full_width - ul_width_extra) - trueInnerWidth(ul) );
                    // I hate to do this but for some reason Firefox (v13.0) and IE are always
                    // one pixel off when rendering. So this is a quick fix for that.
                    if ($.browser.mozilla || $.browser.msie) {
                        li_last_width = li_last_width - 1;
                    }
                    // Add the leftovers to the last navigation item
                    li_last.css({ 'width' : li_last_width + 'px' });

                } else {
                    // Every modern browser supports the "display: table" method
                    // so this is the best way to do it for them.
                    ul.css({ 'display' : 'table', 'float' : 'none', 'width' : '100%' });
                    li.css({ 'display' : 'table-cell', 'float' : 'none' });
                }
                ul_wrap.addClass("horizontalNav-processed");
            }

        }); // @end of return this.each()

    };

    $.fn.horizontalNav.defaults = {
        responsive : true,
        responsiveDelay : 100,
        tableDisplay : true,
        minimumItems : 0
    };

})(jQuery);<|endoftext|>


<commit_before>var appDir = Ti.Filesystem.resourcesDirectory,
	_require = require,
	path;

module.exports = function(dirname, _module) {
	var req = function(p) {

		// attempt to load cache from raw path
		var cached = req.cache[p];
		if (cached) { return cached; }

		// attempt to load cache from resolved path
		var resolved = req.resolve(p);
		cached = req.cache[resolved];
		if (cached) { return cached; }

		// load as JSON file
		var loaded;
		if (/\.json$/.test(resolved)) {
			var jsonFile = Ti.Filesystem.getFile(appDir, resolved);
			loaded = JSON.parse(jsonFile.read().text);

		// load as module
		} else {
			loaded = _require(resolved.replace(/\.(?:js|json)$/, ''));
			req.cache[resolved] = req.cache[p] = loaded;
		}

		return loaded;
	};

	// cache loaded modules
	req.cache = {};

	// list of valid node_modules paths for this require
	req.paths = getRequirePaths(dirname);

	// reference to the main module (app.js)
	req.main = __mainModule; // jshint ignore:line

	// resolve the given module id to a path
	req.resolve = function resolve(p) {
		var isNodeModule = false,
			rawPath;

		if (p.indexOf('/') === 0) {
			rawPath = p;
		} else if (p.indexOf('../') === 0 || p.indexOf('./') === 0) {
			rawPath = dirname + (dirname === '/' ? '' : '/') + p;
		} else {
			isNodeModule = true;
			rawPath = p;
		}

		var resolvedPath = path.resolve(rawPath),
			modulePath;

		if (isNodeModule) {
			for (var i = 0, len = req.paths.length; i < len; i++) {
				var newResolvedPath = req.paths[i] + resolvedPath;
				modulePath = loadAsFile(newResolvedPath) || loadAsDirectory(newResolvedPath);
				if (modulePath) { break; }
			}
		} else {
			modulePath = loadAsFile(resolvedPath) || loadAsDirectory(resolvedPath);
		}

		if (!modulePath) {
			var err = new Error("Cannot find module '" + p + "' loaded from '" + _module.filename + "'");
			err.code = 'MODULE_NOT_FOUND';
			throw err;
		}
		return modulePath;
	};

	return req;
};

function getRequirePaths(dirname) {
	if (dirname === '/') {
		return ['/node_modules'];
	}

	var paths = [];
	var parts = dirname.split('/');
	for (var i = 0, len = parts.length; i < len; i++) {
		var p = parts.slice(0, len-i);
		paths.push(p.join('/') + '/node_modules');
	}

	return paths;
}

function loadAsFile(filepath) {
	var checks = [filepath,filepath+'.js',filepath+'.json'];
	for (var i = 0, len = checks.length; i < len; i++) {
		if (getLoadableFile(checks[i])) {
			return checks[i];
		}
	}
}

function loadAsDirectory(filepath) {
	if (!Ti.Filesystem.getFile(appDir, filepath).isDirectory()) {
		return;
	}

	// require from the package.json "main" path
	var packageJsonFile = Ti.Filesystem.getFile(appDir, filepath + '/package.json');
	if (packageJsonFile.exists()) {
		var json = JSON.parse(packageJsonFile.read().text);
		if (json.main) {
			return path.resolve(filepath + '/' + json.main);
		}
	}

	// load from index.js in the directory
	var indexFile = Ti.Filesystem.getFile(appDir, filepath + '/index.js');
	if (indexFile.exists()) {
		return filepath + '/index.js';
	}
}

function getLoadableFile(filepath) {
	var file = Ti.Filesystem.getFile(appDir, filepath);
	if (file.exists() && !file.isDirectory()) {
		return file;
	}
}

// process shim
var process = {
	cwd: function() {
		return '/';
	}
};

// Copyright Joyent, Inc. and other Node contributors.
//
// 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.
path = (function() {
	var exports = {};

	function normalizeArray(parts, allowAboveRoot) {
		// if the path tries to go above the root, `up` ends up > 0
		var up = 0;
		for (var i = parts.length - 1; i >= 0; i--) {
			var last = parts[i];
			if (last === '.') {
				parts.splice(i, 1);
			} else if (last === '..') {
				parts.splice(i, 1);
				up++;
			} else if (up) {
				parts.splice(i, 1);
				up--;
			}
		}

		// if the path is allowed to go above the root, restore leading ..s
		if (allowAboveRoot) {
			for (; up--; up) {
				parts.unshift('..');
			}
		}

		return parts;
	}

	function filter (xs, f) {
		if (xs.filter) { return xs.filter(f); }
		var res = [];
		for (var i = 0; i < xs.length; i++) {
			if (f(xs[i], i, xs)) { res.push(xs[i]); }
		}
		return res;
	}

	exports.resolve = function() {
		var resolvedPath = '',
			resolvedAbsolute = false;

		for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
			var path = (i >= 0) ? arguments[i] : process.cwd();

			// Skip empty and invalid entries
			if (typeof path !== 'string') {
				throw new TypeError('Arguments to path.resolve must be strings');
			} else if (!path) {
				continue;
			}

			resolvedPath = path + '/' + resolvedPath;
			resolvedAbsolute = path.charAt(0) === '/';
		}

		// At this point the path should be resolved to a full absolute path, but
		// handle relative paths to be safe (might happen when process.cwd() fails)

		// Normalize the path
		resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) {
			return !!p;
		}), !resolvedAbsolute).join('/');

		return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
	};

	return exports;
})();
<commit_msg>clean path<commit_after>var appDir = Ti.Filesystem.resourcesDirectory,
	_require = require,
	path;

module.exports = function(dirname, _module) {
	var req = function(p) {

		// attempt to load cache from raw path
		var cached = req.cache[p];
		if (cached) { return cached; }

		// attempt to load cache from resolved path
		var resolved = req.resolve(p);
		cached = req.cache[resolved];
		if (cached) { return cached; }

		// load as JSON file
		var loaded;
		if (/\.json$/.test(resolved)) {
			var jsonFile = Ti.Filesystem.getFile(appDir, resolved);
			loaded = JSON.parse(jsonFile.read().text);

		// load as module
		} else {
			loaded = _require(resolved.replace(/\.(?:js|json)$/, ''));
			req.cache[resolved] = req.cache[p] = loaded;
		}

		return loaded;
	};

	// cache loaded modules
	req.cache = {};

	// list of valid node_modules paths for this require
	req.paths = getRequirePaths(dirname);

	// reference to the main module (app.js)
	req.main = __mainModule; // jshint ignore:line

	// resolve the given module id to a path
	req.resolve = function resolve(p) {
		var isNodeModule = false,
			rawPath;

		if (p.indexOf('/') === 0) {
			rawPath = p;
		} else if (p.indexOf('../') === 0 || p.indexOf('./') === 0) {
			rawPath = dirname + (dirname === '/' ? '' : '/') + p;
		} else {
			isNodeModule = true;
			rawPath = p;
		}

		var resolvedPath = path.resolve(rawPath),
			modulePath;

		if (isNodeModule) {
			for (var i = 0, len = req.paths.length; i < len; i++) {
				var newResolvedPath = req.paths[i] + resolvedPath;
				modulePath = loadAsFile(newResolvedPath) || loadAsDirectory(newResolvedPath);
				if (modulePath) { break; }
			}
		} else {
			modulePath = loadAsFile(resolvedPath) || loadAsDirectory(resolvedPath);
		}

		if (!modulePath) {
			var err = new Error("Cannot find module '" + p + "' loaded from '" + _module.filename + "'");
			err.code = 'MODULE_NOT_FOUND';
			throw err;
		}
		return modulePath;
	};

	return req;
};

function getRequirePaths(dirname) {
	if (dirname === '/') {
		return ['/node_modules'];
	}

	var paths = [];
	var parts = dirname.split('/');
	for (var i = 0, len = parts.length; i < len; i++) {
		var p = parts.slice(0, len-i);
		paths.push(p.join('/') + '/node_modules');
	}

	return paths;
}

function loadAsFile(filepath) {
	var checks = [filepath,filepath+'.js',filepath+'.json'];
	for (var i = 0, len = checks.length; i < len; i++) {
		if (getLoadableFile(checks[i])) {
			return checks[i];
		}
	}
}

function loadAsDirectory(filepath) {
	if (!Ti.Filesystem.getFile(appDir, filepath).isDirectory()) {
		return;
	}

	// require from the package.json "main" path
	var packageJsonFile = Ti.Filesystem.getFile(appDir, filepath + '/package.json');
	if (packageJsonFile.exists()) {
		var json = JSON.parse(packageJsonFile.read().text);
		if (json.main) {
			return path.resolve(filepath + '/' + json.main);
		}
	}

	// load from index.js in the directory
	var indexFile = Ti.Filesystem.getFile(appDir, filepath + '/index.js');
	if (indexFile.exists()) {
		return filepath + '/index.js';
	}
}

function getLoadableFile(filepath) {
	var file = Ti.Filesystem.getFile(appDir, filepath.replace(/^\//, ''));
	if (file.exists() && !file.isDirectory()) {
		return file;
	}
}

// process shim
var process = {
	cwd: function() {
		return '/';
	}
};

// Copyright Joyent, Inc. and other Node contributors.
//
// 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.
path = (function() {
	var exports = {};

	function normalizeArray(parts, allowAboveRoot) {
		// if the path tries to go above the root, `up` ends up > 0
		var up = 0;
		for (var i = parts.length - 1; i >= 0; i--) {
			var last = parts[i];
			if (last === '.') {
				parts.splice(i, 1);
			} else if (last === '..') {
				parts.splice(i, 1);
				up++;
			} else if (up) {
				parts.splice(i, 1);
				up--;
			}
		}

		// if the path is allowed to go above the root, restore leading ..s
		if (allowAboveRoot) {
			for (; up--; up) {
				parts.unshift('..');
			}
		}

		return parts;
	}

	function filter (xs, f) {
		if (xs.filter) { return xs.filter(f); }
		var res = [];
		for (var i = 0; i < xs.length; i++) {
			if (f(xs[i], i, xs)) { res.push(xs[i]); }
		}
		return res;
	}

	exports.resolve = function() {
		var resolvedPath = '',
			resolvedAbsolute = false;

		for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
			var path = (i >= 0) ? arguments[i] : process.cwd();

			// Skip empty and invalid entries
			if (typeof path !== 'string') {
				throw new TypeError('Arguments to path.resolve must be strings');
			} else if (!path) {
				continue;
			}

			resolvedPath = path + '/' + resolvedPath;
			resolvedAbsolute = path.charAt(0) === '/';
		}

		// At this point the path should be resolved to a full absolute path, but
		// handle relative paths to be safe (might happen when process.cwd() fails)

		// Normalize the path
		resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) {
			return !!p;
		}), !resolvedAbsolute).join('/');

		return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
	};

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


<commit_before>// Utility for creating objects in older browsers
if ( typeof Object.create !== 'function' )
{
  Object.create = function( obj )
  {
    function F() {}
    F.prototype = obj;
    return new F();
  };
}

(function($, window, document, undefined)
{
  var pluginName = 'panelSnap';

  var pluginObject = {
    isMouseDown: false,
    isSnapping: false,
    scrollOffset: 0,

    init: function(options, container)
    {
      var self = this;

      self.$window = $(window);
      self.$document = $(document);

      self.container = container;
      self.$container = $(container);

      self.options = $.extend(true, {}, $.fn.panelSnap.options, options);

      self.bind();

      return self;
    },

    bind: function()
    {
      var self = this;

      self.$document.on('scrollstop' + self.options.nameSpace, $.proxy(function()
      {
        self.processScroll();
      }, self));
      self.$document.on('scrollstart' + self.options.nameSpace, $.proxy(function()
      {
        self.initScroll();
      }, self));
      self.$document.on('mousewheel' + self.options.nameSpace, $.proxy(function()
      {
        self.holdScroll();
      }, self));
      self.$document.on('mousedown' + self.options.nameSpace, $.proxy(function()
      {
        self.mouseDown();
      }, self));
      self.$document.on('mouseup' + self.options.nameSpace, $.proxy(function()
      {
        self.mouseUp();
      }, self));
      self.$window.on('resize' + self.options.nameSpace, $.proxy(function()
      {
        self.processScroll();
      }, self));

      if(self.options.$menu !== false)
      {
        $(a, self.options.$menu).on('click' + self.options.nameSpace, $.proxy(function()
        {
          self.captureMenuClick();
        }, self));
      }
    },

    destroy: function()
    {
      var self = this;

      // Gotta love namespaced events!
      self.$document.off(self.options.nameSpace);
      self.$window.off(self.options.nameSpace);

      if(self.options.$menu !== false)
      {
        $(a, self.options.$menu).off(self.options.nameSpace);
      }
    },

    captureMenuClick: function(e)
    {
      var self = this;

      var $target = $(panelSelector + '.' + $(self).data('panel'), self.settings.$container);
      snapToPanel($target);
    },

    holdScroll: function(e)
    {
      var self = this;

      self.$container.stop(true);
    },

    initScroll: function(e)
    {
      var self = this;

      self.scrollOffset = self.$container.scrollTop();
    },

    processScroll: function(e)
    {
      var self = this;

      if(self.isMouseDown)
      {
        console.log(self.isMouseDown);
        self.$container.one('mouseup', self.processScroll);
        return false;
      }

      if(self.isSnapping)
      {
        return false;
      }

      var interval = self.$container.height();
      var offset = self.$container.scrollTop();
      var scrollDifference = offset - self.scrollOffset;

      var child_number;
      if(scrollDifference < -self.options.directionThreshold && scrollDifference > -interval)
      {
        child_number = Math.floor(offset / interval);
      }
      else if(scrollDifference > self.options.directionThreshold && scrollDifference < interval)
      {
        child_number = Math.ceil(offset / interval);
      }
      else
      {
        child_number = Math.round(offset / interval);
      }

      var target_class = $(self.options.panelSelector, self.$container)[child_number].className;
      var $target = $(self.options.panelSelector + '.' + target_class, self.$container);

      self.snapToPanel($target);
    },

    mouseDown: function(e)
    {
      var self = this;

      self.isMouseDown = true;
    },

    mouseUp: function(e)
    {
      var self = this;

      self.isMouseDown = false;
    },

    snapToPanel: function($target)
    {
      var self = this;

      self.isSnapping = true;

      var scrollTop = $target.offset().top;

      self.$container.animate(
      {
        scrollTop: scrollTop
      }, self.options.slideSpeed, function()
      {
        self.isSnapping = false;
      });

      if(self.options.$menu !== false)
      {
        $('a.active', self.options.$menu).removeClass('active');
        var $activeItem = $('a[data-panel=' + $target.attr('class') + ']', self.options.$menu);
        $activeItem.addClass('active');
      }

      self.options.onSlideFinish.call(self);
    }
  };

  $.fn[pluginName] = function(options)
  {
    return this.each(function()
    {
      var name = 'plugin_' + pluginName;
      var pluginInstance = $.data(self, name);
      if(typeof options === 'object' || options === 'init' || ! options)
      {
        if(!pluginInstance)
        {
          pluginInstance = $.data(this, name, Object.create(pluginObject).init(options, this));
        }
      }
      else if(!pluginInstance)
      {
        $.error('Plugin was not initialized for self object');
        return true;
      }
      else if(typeof options === 'string' && methods[options])
      {
        method = options;
        options = Array.prototype.slice.call(arguments, 1);
        pluginInstance[method](options);
      }
      else
      {
        $.error('Method ' +  options + ' does not exist on jQuery.panelSnap.');
        return true;
      }
    });
  };

  $.fn.panelSnap.options = {
    $menu: false,
    panelSelector: 'section',
    nameSpace: '.panelSnap',
    onSlideStart: function(){},
    onSlideFinish: function(){},
    directionThreshold: 50,
    slideSpeed: 200
  };

})(jQuery, window, document);<commit_msg>Fixes a bug where interupting snapping would cause the plugin to stop working.<commit_after>// Utility for creating objects in older browsers
if ( typeof Object.create !== 'function' )
{
  Object.create = function( obj )
  {
    function F() {}
    F.prototype = obj;
    return new F();
  };
}

(function($, window, document, undefined)
{
  var pluginName = 'panelSnap';

  var pluginObject = {
    isMouseDown: false,
    isSnapping: false,
    scrollOffset: 0,

    init: function(options, container)
    {
      var self = this;

      self.$window = $(window);
      self.$document = $(document);

      self.container = container;
      self.$container = $(container);

      self.options = $.extend(true, {}, $.fn.panelSnap.options, options);

      self.bind();

      return self;
    },

    bind: function()
    {
      var self = this;

      self.$document.on('scrollstop' + self.options.nameSpace, $.proxy(function()
      {
        self.processScroll();
      }, self));
      self.$document.on('scrollstart' + self.options.nameSpace, $.proxy(function()
      {
        self.initScroll();
      }, self));
      self.$document.on('mousewheel' + self.options.nameSpace, $.proxy(function()
      {
        self.holdScroll();
      }, self));
      self.$document.on('mousedown' + self.options.nameSpace, $.proxy(function()
      {
        self.mouseDown();
      }, self));
      self.$document.on('mouseup' + self.options.nameSpace, $.proxy(function()
      {
        self.mouseUp();
      }, self));
      self.$window.on('resize' + self.options.nameSpace, $.proxy(function()
      {
        self.processScroll();
      }, self));

      if(self.options.$menu !== false)
      {
        $(a, self.options.$menu).on('click' + self.options.nameSpace, $.proxy(function()
        {
          self.captureMenuClick();
        }, self));
      }
    },

    destroy: function()
    {
      var self = this;

      // Gotta love namespaced events!
      self.$document.off(self.options.nameSpace);
      self.$window.off(self.options.nameSpace);

      if(self.options.$menu !== false)
      {
        $(a, self.options.$menu).off(self.options.nameSpace);
      }
    },

    captureMenuClick: function(e)
    {
      var self = this;

      var $target = $(panelSelector + '.' + $(self).data('panel'), self.settings.$container);
      snapToPanel($target);
    },

    holdScroll: function(e)
    {
      var self = this;

      self.$container.stop(true);
      self.isSnapping = false;
    },

    initScroll: function(e)
    {
      var self = this;

      self.scrollOffset = self.$container.scrollTop();
    },

    processScroll: function(e)
    {
      var self = this;

      if(self.isMouseDown)
      {
        console.log(self.isMouseDown);
        self.$container.one('mouseup', self.processScroll);
        return false;
      }

      if(self.isSnapping)
      {
        return false;
      }

      var interval = self.$container.height();
      var offset = self.$container.scrollTop();
      var scrollDifference = offset - self.scrollOffset;

      var child_number;
      if(scrollDifference < -self.options.directionThreshold && scrollDifference > -interval)
      {
        child_number = Math.floor(offset / interval);
      }
      else if(scrollDifference > self.options.directionThreshold && scrollDifference < interval)
      {
        child_number = Math.ceil(offset / interval);
      }
      else
      {
        child_number = Math.round(offset / interval);
      }

      var target_class = $(self.options.panelSelector, self.$container)[child_number].className;
      var $target = $(self.options.panelSelector + '.' + target_class, self.$container);

      self.snapToPanel($target);
    },

    mouseDown: function(e)
    {
      var self = this;

      self.isMouseDown = true;
    },

    mouseUp: function(e)
    {
      var self = this;

      self.isMouseDown = false;
    },

    snapToPanel: function($target)
    {
      var self = this;

      self.isSnapping = true;

      var scrollTop = $target.offset().top;

      self.$container.animate(
      {
        scrollTop: scrollTop
      }, self.options.slideSpeed, function()
      {
        self.isSnapping = false;
      });

      if(self.options.$menu !== false)
      {
        $('a.active', self.options.$menu).removeClass('active');
        var $activeItem = $('a[data-panel=' + $target.attr('class') + ']', self.options.$menu);
        $activeItem.addClass('active');
      }

      self.options.onSlideFinish.call(self);
    }
  };

  $.fn[pluginName] = function(options)
  {
    return this.each(function()
    {
      var name = 'plugin_' + pluginName;
      var pluginInstance = $.data(self, name);
      if(typeof options === 'object' || options === 'init' || ! options)
      {
        if(!pluginInstance)
        {
          pluginInstance = $.data(this, name, Object.create(pluginObject).init(options, this));
        }
      }
      else if(!pluginInstance)
      {
        $.error('Plugin was not initialized for self object');
        return true;
      }
      else if(typeof options === 'string' && methods[options])
      {
        method = options;
        options = Array.prototype.slice.call(arguments, 1);
        pluginInstance[method](options);
      }
      else
      {
        $.error('Method ' +  options + ' does not exist on jQuery.panelSnap.');
        return true;
      }
    });
  };

  $.fn.panelSnap.options = {
    $menu: false,
    panelSelector: 'section',
    nameSpace: '.panelSnap',
    onSlideStart: function(){},
    onSlideFinish: function(){},
    directionThreshold: 50,
    slideSpeed: 200
  };

})(jQuery, window, document);<|endoftext|>