<commit_before>/*
Copyright 2015, 2016 OpenMarket Ltd

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.
*/

'use strict';

var MatrixClientPeg = require("./MatrixClientPeg");
var TextForEvent = require('./TextForEvent');
var Avatar = require('./Avatar');
var dis = require("./dispatcher");

/*
 * Dispatches:
 * {
 *   action: "notifier_enabled",
 *   value: boolean
 * }
 */

var Notifier = {

    notificationMessageForEvent: function(ev) {
        return TextForEvent.textForEvent(ev);
    },

    _displayPopupNotification: function(ev, room) {
        if (!global.Notification || global.Notification.permission != 'granted') {
            return;
        }
        if (global.document.hasFocus()) {
            return;
        }

        var msg = this.notificationMessageForEvent(ev);
        if (!msg) return;

        var title;
        if (!ev.sender ||  room.name == ev.sender.name) {
            title = room.name;
            // notificationMessageForEvent includes sender,
            // but we already have the sender here
            if (ev.getContent().body) msg = ev.getContent().body;
        } else if (ev.getType() == 'm.room.member') {
            // context is all in the message here, we don't need
            // to display sender info
            title = room.name;
        } else if (ev.sender) {
            title = ev.sender.name + " (" + room.name + ")";
            // notificationMessageForEvent includes sender,
            // but we've just out sender in the title
            if (ev.getContent().body) msg = ev.getContent().body;
        }

        var avatarUrl = ev.sender ? Avatar.avatarUrlForMember(
            ev.sender, 40, 40, 'crop'
        ) : null;

        var notification = new global.Notification(
            title,
            {
                "body": msg,
                "icon": avatarUrl,
                "tag": "matrixreactsdk"
            }
        );

        notification.onclick = function() {
            dis.dispatch({
                action: 'view_room',
                room_id: room.roomId
            });
            global.focus();
        };

        global.setTimeout(function() {
            notification.close();
        }, 5 * 1000);
    },

    _playAudioNotification: function(ev, room) {
        var e = document.getElementById("messageAudio");
        if (e) {
            e.load();
            e.play();
        };
    },

    start: function() {
        this.boundOnRoomTimeline = this.onRoomTimeline.bind(this);
        this.boundOnSyncStateChange = this.onSyncStateChange.bind(this);
        MatrixClientPeg.get().on('Room.timeline', this.boundOnRoomTimeline);
        MatrixClientPeg.get().on("sync", this.boundOnSyncStateChange);
        this.toolbarHidden = false;
        this.isPrepared = false;
    },

    stop: function() {
        if (MatrixClientPeg.get()) {
            MatrixClientPeg.get().removeListener('Room.timeline', this.boundOnRoomTimeline);
            MatrixClientPeg.get().removeListener('sync', this.boundOnSyncStateChange);
        }
        this.isPrepared = false;
    },

    supportsDesktopNotifications: function() {
        return !!global.Notification;
    },

    havePermission: function() {
        if (!this.supportsDesktopNotifications()) return false;
        return global.Notification.permission == 'granted';
    },

    isPermissionDefault: function() {
        if (!this.supportsDesktopNotifications()) return false;
        return global.Notification.permission == 'default';
    },

    showToolbar: function() {
        // Check localStorage for any such meta data
        if (global.localStorage) {
            if (global.localStorage.getItem('notifications_hidden') === 'true')
                return false;
        }

        // Check if permission is granted by any chance.
        if (this.havePermission()) return false;

        // means the permission is blocked
        if (!this.isPermissionDefault()) return false;
        return true;
    },

    setEnabled: function(enable, callback) {
        // make sure that we persist the current setting audio_enabled setting
        // before changing anything
        if (global.localStorage) {
            if(global.localStorage.getItem('audio_notifications_enabled') == null) {
                this.setAudioEnabled(this.isEnabled());
            }
        }

        if(enable) {
            // Case when we do not have the permission as 'granted'
            if (this.isPermissionDefault()) {
                // Attempt to get permission from user
                var _this = this;
                global.Notification.requestPermission().then(function(result) {
                    if (result === 'denied') {
                        dis.dispatch({
                            action: "notifier_enabled",
                            value: false
                        });
                        _this.setToolbarHidden(true);
                        return;
                    }
                    if (result === 'default') {
                        // The permission request was dismissed
                        return;
                    }

                    if (callback) callback();
                    dis.dispatch({
                        action: "notifier_enabled",
                        value: true
                    });

                    if (!global.localStorage) return;
                    global.localStorage.setItem('notifications_enabled', 'true');
                });
            }
        } else {
            if (!global.localStorage) return;
            global.localStorage.setItem('notifications_enabled', 'false');
            dis.dispatch({
                action: "notifier_enabled",
                value: false
            });
        }
    },

    isEnabled: function() {
        if (!this.havePermission()) return false;

        if (!global.localStorage) return true;

        var enabled = global.localStorage.getItem('notifications_enabled');
        if (enabled === null) return true;
        return enabled === 'true';
    },

    setAudioEnabled: function(enable) {
        if (!global.localStorage) return;
        global.localStorage.setItem('audio_notifications_enabled',
                                    enable ? 'true' : 'false');
    },

    isAudioEnabled: function(enable) {
        if (!global.localStorage) return true;
        var enabled = global.localStorage.getItem(
            'audio_notifications_enabled');
        // default to true if the popups are enabled
        if (enabled === null) return this.isEnabled();
        return enabled === 'true';
    },

     setToolbarHidden: function(hidden, persistent) {
        this.toolbarHidden = hidden;
        dis.dispatch({
            action: "notifier_enabled",
            value: this.isEnabled()
        });

        if (persistent === true)
            this.setToolbarPersistantHidden();
    },

    setToolbarPersistantHidden: function() {
        // update the info to localStorage
        if (global.localStorage) {
            global.localStorage.setItem('notifications_hidden', 'true');
        }
    },

    isToolbarHidden: function() {
        return this.toolbarHidden;
    },

    onSyncStateChange: function(state) {
        if (state === "PREPARED" || state === "SYNCING") {
            this.isPrepared = true;
        }
        else if (state === "STOPPED" || state === "ERROR") {
            this.isPrepared = false;
        }
    },

    onRoomTimeline: function(ev, room, toStartOfTimeline) {
        if (toStartOfTimeline) return;
        if (!this.isPrepared) return; // don't alert for any messages initially
        if (ev.sender && ev.sender.userId == MatrixClientPeg.get().credentials.userId) return;

        var actions = MatrixClientPeg.get().getPushActionsForEvent(ev);
        if (actions && actions.notify) {
            if (this.isEnabled()) {
                this._displayPopupNotification(ev, room);
            }
            if (actions.tweaks.sound && this.isAudioEnabled()) {
                this._playAudioNotification(ev, room);
            }
        }
    }
};

if (!global.mxNotifier) {
    global.mxNotifier = Notifier;
}

module.exports = global.mxNotifier;
<commit_msg>Some style fixes<commit_after>/*
Copyright 2015, 2016 OpenMarket Ltd

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.
*/

'use strict';

var MatrixClientPeg = require("./MatrixClientPeg");
var TextForEvent = require('./TextForEvent');
var Avatar = require('./Avatar');
var dis = require("./dispatcher");

/*
 * Dispatches:
 * {
 *   action: "notifier_enabled",
 *   value: boolean
 * }
 */

var Notifier = {

    notificationMessageForEvent: function(ev) {
        return TextForEvent.textForEvent(ev);
    },

    _displayPopupNotification: function(ev, room) {
        if (!global.Notification || global.Notification.permission != 'granted') {
            return;
        }
        if (global.document.hasFocus()) {
            return;
        }

        var msg = this.notificationMessageForEvent(ev);
        if (!msg) return;

        var title;
        if (!ev.sender ||  room.name == ev.sender.name) {
            title = room.name;
            // notificationMessageForEvent includes sender,
            // but we already have the sender here
            if (ev.getContent().body) msg = ev.getContent().body;
        } else if (ev.getType() == 'm.room.member') {
            // context is all in the message here, we don't need
            // to display sender info
            title = room.name;
        } else if (ev.sender) {
            title = ev.sender.name + " (" + room.name + ")";
            // notificationMessageForEvent includes sender,
            // but we've just out sender in the title
            if (ev.getContent().body) msg = ev.getContent().body;
        }

        var avatarUrl = ev.sender ? Avatar.avatarUrlForMember(
            ev.sender, 40, 40, 'crop'
        ) : null;

        var notification = new global.Notification(
            title,
            {
                "body": msg,
                "icon": avatarUrl,
                "tag": "matrixreactsdk"
            }
        );

        notification.onclick = function() {
            dis.dispatch({
                action: 'view_room',
                room_id: room.roomId
            });
            global.focus();
        };

        global.setTimeout(function() {
            notification.close();
        }, 5 * 1000);
    },

    _playAudioNotification: function(ev, room) {
        var e = document.getElementById("messageAudio");
        if (e) {
            e.load();
            e.play();
        };
    },

    start: function() {
        this.boundOnRoomTimeline = this.onRoomTimeline.bind(this);
        this.boundOnSyncStateChange = this.onSyncStateChange.bind(this);
        MatrixClientPeg.get().on('Room.timeline', this.boundOnRoomTimeline);
        MatrixClientPeg.get().on("sync", this.boundOnSyncStateChange);
        this.toolbarHidden = false;
        this.isPrepared = false;
    },

    stop: function() {
        if (MatrixClientPeg.get()) {
            MatrixClientPeg.get().removeListener('Room.timeline', this.boundOnRoomTimeline);
            MatrixClientPeg.get().removeListener('sync', this.boundOnSyncStateChange);
        }
        this.isPrepared = false;
    },

    supportsDesktopNotifications: function() {
        return !!global.Notification;
    },

    havePermission: function() {
        if (!this.supportsDesktopNotifications()) return false;
        return global.Notification.permission == 'granted';
    },

    isPermissionDefault: function() {
        if (!this.supportsDesktopNotifications()) return false;
        return global.Notification.permission == 'default';
    },

    showToolbar: function() {
        // Check localStorage for any such meta data
        if (global.localStorage) {
            if (global.localStorage.getItem('notifications_hidden') === 'true')
                return false;
        }

        // Check if permission is granted by any chance.
        if (this.havePermission()) return false;

        // means the permission is blocked
        if (!this.isPermissionDefault()) return false;
        return true;
    },

    setEnabled: function(enable, callback) {
        // make sure that we persist the current setting audio_enabled setting
        // before changing anything
        if (global.localStorage) {
            if(global.localStorage.getItem('audio_notifications_enabled') == null) {
                this.setAudioEnabled(this.isEnabled());
            }
        }

        if(enable) {
            // Case when we do not have the permission as 'granted'
            if (this.isPermissionDefault()) {
                // Attempt to get permission from user
                var self = this;
                global.Notification.requestPermission().then(function(result) {
                    if (result === 'denied') {
                        dis.dispatch({
                            action: "notifier_enabled",
                            value: false
                        });
                        self.setToolbarHidden(true);
                        return;
                    }
                    if (result === 'default') {
                        // The permission request was dismissed
                        return;
                    }

                    if (callback) callback();
                    dis.dispatch({
                        action: "notifier_enabled",
                        value: true
                    });

                    if (!global.localStorage) return;
                    global.localStorage.setItem('notifications_enabled', 'true');
                });
            }
        } else {
            if (!global.localStorage) return;
            global.localStorage.setItem('notifications_enabled', 'false');
            dis.dispatch({
                action: "notifier_enabled",
                value: false
            });
        }
    },

    isEnabled: function() {
        if (!this.havePermission()) return false;

        if (!global.localStorage) return true;

        var enabled = global.localStorage.getItem('notifications_enabled');
        if (enabled === null) return true;
        return enabled === 'true';
    },

    setAudioEnabled: function(enable) {
        if (!global.localStorage) return;
        global.localStorage.setItem('audio_notifications_enabled',
                                    enable ? 'true' : 'false');
    },

    isAudioEnabled: function(enable) {
        if (!global.localStorage) return true;
        var enabled = global.localStorage.getItem(
            'audio_notifications_enabled');
        // default to true if the popups are enabled
        if (enabled === null) return this.isEnabled();
        return enabled === 'true';
    },

     setToolbarHidden: function(hidden, persistent) {
        this.toolbarHidden = hidden;
        dis.dispatch({
            action: "notifier_enabled",
            value: this.isEnabled()
        });

        if (persistent) {
            this.setToolbarPersistantHidden();
        }
    },

    setToolbarPersistantHidden: function() {
        // update the info to localStorage
        if (global.localStorage) {
            global.localStorage.setItem('notifications_hidden', 'true');
        }
    },

    isToolbarHidden: function() {
        return this.toolbarHidden;
    },

    onSyncStateChange: function(state) {
        if (state === "PREPARED" || state === "SYNCING") {
            this.isPrepared = true;
        }
        else if (state === "STOPPED" || state === "ERROR") {
            this.isPrepared = false;
        }
    },

    onRoomTimeline: function(ev, room, toStartOfTimeline) {
        if (toStartOfTimeline) return;
        if (!this.isPrepared) return; // don't alert for any messages initially
        if (ev.sender && ev.sender.userId == MatrixClientPeg.get().credentials.userId) return;

        var actions = MatrixClientPeg.get().getPushActionsForEvent(ev);
        if (actions && actions.notify) {
            if (this.isEnabled()) {
                this._displayPopupNotification(ev, room);
            }
            if (actions.tweaks.sound && this.isAudioEnabled()) {
                this._playAudioNotification(ev, room);
            }
        }
    }
};

if (!global.mxNotifier) {
    global.mxNotifier = Notifier;
}

module.exports = global.mxNotifier;
<|endoftext|>


<commit_before>/**
 * File name and definition search for the Cloud9
 *
 * @copyright 2013, Ajax.org B.V.
 */

define(function(require, exports, module) {

var Heap = require("./heap");

var fileTypes = {
    "js": 1, "json": 11, "css": 5, "less": 5, "scss": 5, "sass": 5, "xml": 11,
    "rdf": 15, "rss": 15, "svg": 5, "wsdl": 11, "xslt": 5, "atom": 5,
    "mathml": 5, "mml": 5, "php": 1, "phtml": 1, "html": 5, "xhtml": 5,
    "coffee": 1, "py": 1, "ru": 1, "gemspec": 5, "rake": 5, "rb": 1, "c": 1,
    "cc": 1, "cpp": 1, "cxx": 1, "h": 2, "hh": 2, "hpp": 2, "cs": 1, "java": 1,
    "clj": 1, "groovy": 1, "logic": 1, "lql": 1, "scala": 1, "ml": 1, "mli": 1, "md": 2,
    "markdown": 2, "textile": 5, "latex": 5, "tex": 5, "ltx": 5, "lua": 1,
    "pl": 1, "pm": 2, "ps1": 1, "cfm": 1, "sql": 2, "sh": 1, "bash": 1,
    "bmp": 10, "djv": 10, "djvu": 10, "gif": 10, "ico": 10, "jpg": 10,
    "jpeg": 10, "pbm": 10, "pgm": 10, "png": 10, "pnm": 10, "ppm": 10,
    "psd": 10, "tiff": 10, "xbm": 10, "xpm": 10,
    "go": 5, "hx": 5, "yaml": 5, "psql": 2
};

/**
 * Search through a list of filenames.
 */
module.exports.fileSearch = function(filelist, keyword) {
    if (!keyword) keyword = "";

    var klen = keyword.length;
    var keywordLower = keyword.toLowerCase();

    /**
     * FULL MATCHES                              >= 50
     *   full filename with extension             1000
     *   name part without the extension           201
     *   start of filename                         200
     *   part of filename                          100
     *   depth of path (or length to optimize)     200 - 10 * count("/")
     *   full part of path                          50
     *
     * SCATTERED MATCHES                         <= 45
     *   number of match groups                     20 - 3 * #groups
     *   path depth                                 15 - 2 * count("/")
     *   match diff                                 10 - len(diff)
     *
     * Extension weight                             -1 * lut[ext]
     */

    var type = "value";
    var toS = function(){
        return this[type];
    };

    var newlist = [];
    var l = filelist.length;
    var big = l > 50000;

    var name, res = big ? [] : new Heap(), value, ext, displayName;
    for (var i = 0, s, j, k, q, p, m, n; i < l; i++) {
        displayName = filelist[i];
        name = displayName.toLowerCase();

        // We only add items that have the keyword in it's path
        value = 0;
        if ((j = name.lastIndexOf(keywordLower)) > -1) {
            if (big) {
                res.push(displayName);
                continue;
            }
            // We prioritize ones that have the name in the filename
            if (j > (q = name.lastIndexOf("/"))) {
                k = name.lastIndexOf("/" + keywordLower);
                if (k > -1) {
                    // We give first prio to full filename matches
                    if (name.length == klen + 1 + k)
                        value += 1000;

                    // Then to match of name prior to extension
                    else if (name.lastIndexOf(".") == k + klen + 1)
                        value += 201;

                    // Then to matches from the start of the filename
                    else if (k == q)
                        value += 200;

                    // Then anywhere in the filename
                    else
                        value += 100;
                }

                // The shorter the path depth, the higher prio we give
                value += 200 - Math.min(name.split("/").length * 10, 150);
            }
            // Detect a path search
            else if (keywordLower.indexOf("/") > -1) {
                var idx = keywordLower.lastIndexOf("/");
                var rest = name.substr(j + idx + 1);
                var file = keywordLower.substr(idx + 1);

                // We give prio to full path matches
                if (j === 0)
                    value += 1000;

                // When searching for a file extension,
                if (file.indexOf(".") > -1) {
                    // Prioritize filename matches
                    if (rest.indexOf(file) === 0)
                        value += 200;

                    // The shorter the filename length, the higher prio we give
                    value += 200 - Math.min((rest.length - file.length) * 10, 150);
                }
                // When the full filename is matched, give a higher prio
                else if (rest.substr(file.length).match(/^\..*(\/|$)/))
                    value += 201;
                else
                    value += 50;
            }
            // Then the rest
            else
                value += 50;
        }
        // Check for spatial matches
        else {
            if (big || name.split("/").length > 10)
                continue;

            var result;
            result = matchPath(name, keywordLower);
            if (!result.length || result.length > 20)
                continue;

            var matched = name.substring(result[0].val.length);
            // The less the number of groups matched, the higher prio we give
            value += 20 - Math.min((result.length-2)*3, 19);
            // The shorter the path depth, the higher prio we give
            value += 15 - Math.min(name.split("/").length*2, 14);
            // The shorter the match diff, the higher prio we give
            value += 10 - Math.min(matched.length - keyword.length, 9);
            value += 20; // extension
        }

        if (value > 0) {
            newlist.push(displayName);

            // Check extension
            s = name.lastIndexOf(".");
            if (s > -1)
                value -= 10 * (fileTypes[name.substr(s+1)] || 0) || 20;
            else
                value -= 20;

            if (res.size() === 100 && value > res.min().value)
                res.pop();
            if (res.size() < 100)
                res.push({
                    toString: toS,
                    value: value,
                    name: displayName
                });
        }
    }

    if (big) {
        res.newlist = res;
        return res;
    }

    var ret = [];
    while (res.size())
        ret.unshift(res.pop().name);

    ret.newlist = newlist;

    return ret;
};

// function score (e, term) {
//     var c = 0,
//         d = term.length,
//         f = e.length,
//         g, h, i = 1,
//         j;
//     if (e == term) return 1;
//     for (var k = 0, l, m, n, o, p, q; k < d; ++k) {
//         n = term[k], o = e.indexOf(n.toLowerCase()), p = e.indexOf(n.toUpperCase()), q = Math.min(o, p), m = q > -1 ? q : Math.max(o, p);
//         if (m === -1) {
//             return 0;
//         }
//         l = .1, e[m] === n && (l += .1), m === 0 ? (l += .6, k === 0 && (g = 1)) : e.charAt(m - 1) === " " && (l += .8), e = e.substring(m + 1, f), c += l
//     }
//     return h = c / d, j = (h * (d / f) + h) / 2, j /= i, g && j + .15 < 1 && (j += .15), j
// };

var treeSearch = module.exports.treeSearch = function(tree, keyword, caseInsensitive, results, head, indexProperty) {
    if (caseInsensitive)
        keyword = keyword.toLowerCase();
    results = results || [];
    head = head || 0;
    for (var i = 0; i < tree.length; i++) {
        var node = tree[i];
        var name = indexProperty
            ? node[indexProperty]
            : node.name || node.label || node.caption || (typeof node == "string" ? node : "");
        if (caseInsensitive)
            name = name.toLowerCase();
        var index = name.indexOf(keyword);
        if (index === -1) {
            if (node.items && !node.keepChildren)
                results = treeSearch(node.items, keyword, caseInsensitive, results, head);
            continue;
        }
        var result = node.clone ? node.clone() : {};
        result.items = node.items
            ? (result.keepChildren
                ? result.items
                : treeSearch(node.items, keyword, caseInsensitive))
            : [];

        if (!node.clone) {
            for (var p in node) {
                if (node[p] && p !== "items")
                    result[p] = node[p];
            }
        }

        if (index === 0) {
            results.splice(head, 0, result);
            head++;
        }
        else {
            results.push(result);
        }
    }
    return results;
};

var matchPath = module.exports.matchPath = function (path, keyword) {
    var result = [];
    var pathSplits = path.split("/");
    // Optimization
    if (pathSplits.length > 4) {
        pathSplits = [pathSplits.slice(0, pathSplits.length - 4).join("/") + "/"]
            .concat(pathSplits.slice(pathSplits.length - 4, pathSplits.length));
        if (pathSplits[0] == "/")
            pathSplits[0] = "";
    }
    var value = "";
    var k, i, j = -1;
    for (k = pathSplits.length-1; k >= 0  && !result.length; k--) {
        value = (k > 0 ? "/" : "") + pathSplits[k] + value;
        // find matched parts
        var matchI = null;
        var missI = null;
        for (i = 0, j = 0; i < value.length && j < keyword.length; i++) {
            if (value[i] === keyword[j]) {
                matchI = matchI === null ? i : matchI;
                j++;
                if (missI !== null) {
                    result.push({ val: value.substring(missI, i) });
                    missI = null;
                }
            }
            else {
                missI = missI === null ? i : missI;
                if (matchI !== null) {
                    result.push({ match: true, val: value.substring(matchI, i)});
                    matchI = null;
                }
            }
        }
        if (j !== keyword.length) {
            result = [];
            continue;
        }

        if (missI !== null)
            result.push({ val: value.substring(missI, i) });
        if (matchI !== null)
            result.push({ match: true, val: value.substring(matchI, i)});
        result.push({ val: value.substring(i, value.length) });
        // Add the first non matched part if exists
        if (k)
            result.unshift({ val: pathSplits.slice(0, k).join('/') });
    }
    return result;
};

});
<commit_msg>Added filtering to branches panel<commit_after>/**
 * File name and definition search for the Cloud9
 *
 * @copyright 2013, Ajax.org B.V.
 */

define(function(require, exports, module) {

var Heap = require("./heap");

var fileTypes = {
    "js": 1, "json": 11, "css": 5, "less": 5, "scss": 5, "sass": 5, "xml": 11,
    "rdf": 15, "rss": 15, "svg": 5, "wsdl": 11, "xslt": 5, "atom": 5,
    "mathml": 5, "mml": 5, "php": 1, "phtml": 1, "html": 5, "xhtml": 5,
    "coffee": 1, "py": 1, "ru": 1, "gemspec": 5, "rake": 5, "rb": 1, "c": 1,
    "cc": 1, "cpp": 1, "cxx": 1, "h": 2, "hh": 2, "hpp": 2, "cs": 1, "java": 1,
    "clj": 1, "groovy": 1, "logic": 1, "lql": 1, "scala": 1, "ml": 1, "mli": 1, "md": 2,
    "markdown": 2, "textile": 5, "latex": 5, "tex": 5, "ltx": 5, "lua": 1,
    "pl": 1, "pm": 2, "ps1": 1, "cfm": 1, "sql": 2, "sh": 1, "bash": 1,
    "bmp": 10, "djv": 10, "djvu": 10, "gif": 10, "ico": 10, "jpg": 10,
    "jpeg": 10, "pbm": 10, "pgm": 10, "png": 10, "pnm": 10, "ppm": 10,
    "psd": 10, "tiff": 10, "xbm": 10, "xpm": 10,
    "go": 5, "hx": 5, "yaml": 5, "psql": 2
};

/**
 * Search through a list of filenames.
 */
module.exports.fileSearch = function(filelist, keyword) {
    if (!keyword) keyword = "";

    var klen = keyword.length;
    var keywordLower = keyword.toLowerCase();

    /**
     * FULL MATCHES                              >= 50
     *   full filename with extension             1000
     *   name part without the extension           201
     *   start of filename                         200
     *   part of filename                          100
     *   depth of path (or length to optimize)     200 - 10 * count("/")
     *   full part of path                          50
     *
     * SCATTERED MATCHES                         <= 45
     *   number of match groups                     20 - 3 * #groups
     *   path depth                                 15 - 2 * count("/")
     *   match diff                                 10 - len(diff)
     *
     * Extension weight                             -1 * lut[ext]
     */

    var type = "value";
    var toS = function(){
        return this[type];
    };

    var newlist = [];
    var l = filelist.length;
    var big = l > 50000;

    var name, res = big ? [] : new Heap(), value, ext, displayName;
    for (var i = 0, s, j, k, q, p, m, n; i < l; i++) {
        displayName = filelist[i];
        name = displayName.toLowerCase();

        // We only add items that have the keyword in it's path
        value = 0;
        if ((j = name.lastIndexOf(keywordLower)) > -1) {
            if (big) {
                res.push(displayName);
                continue;
            }
            // We prioritize ones that have the name in the filename
            if (j > (q = name.lastIndexOf("/"))) {
                k = name.lastIndexOf("/" + keywordLower);
                if (k > -1) {
                    // We give first prio to full filename matches
                    if (name.length == klen + 1 + k)
                        value += 1000;

                    // Then to match of name prior to extension
                    else if (name.lastIndexOf(".") == k + klen + 1)
                        value += 201;

                    // Then to matches from the start of the filename
                    else if (k == q)
                        value += 200;

                    // Then anywhere in the filename
                    else
                        value += 100;
                }

                // The shorter the path depth, the higher prio we give
                value += 200 - Math.min(name.split("/").length * 10, 150);
            }
            // Detect a path search
            else if (keywordLower.indexOf("/") > -1) {
                var idx = keywordLower.lastIndexOf("/");
                var rest = name.substr(j + idx + 1);
                var file = keywordLower.substr(idx + 1);

                // We give prio to full path matches
                if (j === 0)
                    value += 1000;

                // When searching for a file extension,
                if (file.indexOf(".") > -1) {
                    // Prioritize filename matches
                    if (rest.indexOf(file) === 0)
                        value += 200;

                    // The shorter the filename length, the higher prio we give
                    value += 200 - Math.min((rest.length - file.length) * 10, 150);
                }
                // When the full filename is matched, give a higher prio
                else if (rest.substr(file.length).match(/^\..*(\/|$)/))
                    value += 201;
                else
                    value += 50;
            }
            // Then the rest
            else
                value += 50;
        }
        // Check for spatial matches
        else {
            if (big || name.split("/").length > 10)
                continue;

            var result;
            result = matchPath(name, keywordLower);
            if (!result.length || result.length > 20)
                continue;

            var matched = name.substring(result[0].val.length);
            // The less the number of groups matched, the higher prio we give
            value += 20 - Math.min((result.length-2)*3, 19);
            // The shorter the path depth, the higher prio we give
            value += 15 - Math.min(name.split("/").length*2, 14);
            // The shorter the match diff, the higher prio we give
            value += 10 - Math.min(matched.length - keyword.length, 9);
            value += 20; // extension
        }

        if (value > 0) {
            newlist.push(displayName);

            // Check extension
            s = name.lastIndexOf(".");
            if (s > -1)
                value -= 10 * (fileTypes[name.substr(s+1)] || 0) || 20;
            else
                value -= 20;

            if (res.size() === 100 && value > res.min().value)
                res.pop();
            if (res.size() < 100)
                res.push({
                    toString: toS,
                    value: value,
                    name: displayName
                });
        }
    }

    if (big) {
        res.newlist = res;
        return res;
    }

    var ret = [];
    while (res.size())
        ret.unshift(res.pop().name);

    ret.newlist = newlist;

    return ret;
};

// function score (e, term) {
//     var c = 0,
//         d = term.length,
//         f = e.length,
//         g, h, i = 1,
//         j;
//     if (e == term) return 1;
//     for (var k = 0, l, m, n, o, p, q; k < d; ++k) {
//         n = term[k], o = e.indexOf(n.toLowerCase()), p = e.indexOf(n.toUpperCase()), q = Math.min(o, p), m = q > -1 ? q : Math.max(o, p);
//         if (m === -1) {
//             return 0;
//         }
//         l = .1, e[m] === n && (l += .1), m === 0 ? (l += .6, k === 0 && (g = 1)) : e.charAt(m - 1) === " " && (l += .8), e = e.substring(m + 1, f), c += l
//     }
//     return h = c / d, j = (h * (d / f) + h) / 2, j /= i, g && j + .15 < 1 && (j += .15), j
// };

var treeSearch = module.exports.treeSearch = function(tree, keyword, caseInsensitive, results, head, indexProperty) {
    if (caseInsensitive)
        keyword = keyword.toLowerCase();

    results = results || [];
    head = head || 0;

    for (var i = 0; i < tree.length; i++) {
        var node = tree[i];
        var name = indexProperty
            ? node[indexProperty] || ""
            : node.name || node.label || node.caption || (typeof node == "string" ? node : "");
        if (caseInsensitive)
            name = name.toLowerCase();
        var index = name.indexOf(keyword);
        if (index === -1) {
            if (node.items && !node.keepChildren)
                results = treeSearch(node.items, keyword, caseInsensitive, results, head);
            continue;
        }
        var result = node.clone ? node.clone() : {};
        result.items = node.items
            ? (result.keepChildren
                ? result.items
                : treeSearch(node.items, keyword, caseInsensitive))
            : [];

        if (!node.clone) {
            for (var p in node) {
                if (node[p] && p !== "items")
                    result[p] = node[p];
            }
        }

        if (index === 0) {
            results.splice(head, 0, result);
            head++;
        }
        else {
            results.push(result);
        }
    }
    return results;
};

var matchPath = module.exports.matchPath = function (path, keyword) {
    var result = [];
    var pathSplits = path.split("/");
    // Optimization
    if (pathSplits.length > 4) {
        pathSplits = [pathSplits.slice(0, pathSplits.length - 4).join("/") + "/"]
            .concat(pathSplits.slice(pathSplits.length - 4, pathSplits.length));
        if (pathSplits[0] == "/")
            pathSplits[0] = "";
    }
    var value = "";
    var k, i, j = -1;
    for (k = pathSplits.length-1; k >= 0  && !result.length; k--) {
        value = (k > 0 ? "/" : "") + pathSplits[k] + value;
        // find matched parts
        var matchI = null;
        var missI = null;
        for (i = 0, j = 0; i < value.length && j < keyword.length; i++) {
            if (value[i] === keyword[j]) {
                matchI = matchI === null ? i : matchI;
                j++;
                if (missI !== null) {
                    result.push({ val: value.substring(missI, i) });
                    missI = null;
                }
            }
            else {
                missI = missI === null ? i : missI;
                if (matchI !== null) {
                    result.push({ match: true, val: value.substring(matchI, i)});
                    matchI = null;
                }
            }
        }
        if (j !== keyword.length) {
            result = [];
            continue;
        }

        if (missI !== null)
            result.push({ val: value.substring(missI, i) });
        if (matchI !== null)
            result.push({ match: true, val: value.substring(matchI, i)});
        result.push({ val: value.substring(i, value.length) });
        // Add the first non matched part if exists
        if (k)
            result.unshift({ val: pathSplits.slice(0, k).join('/') });
    }
    return result;
};

});
<|endoftext|>


<commit_before>/**
 * Copyright 2013 Facebook, Inc.
 *
 * 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.
 */
"use strict";

/**
 * Using Node-Haste.
 */
var BrowserShimLoader = require('./BrowserShimLoader');
var Haste = require('node-haste/lib/Haste');
var HasteDependencyLoader = require('node-haste/lib/HasteDependencyLoader');
var Modularizer = require('./Modularizer');
var Package = require('./Package');
var ProjectConfigurationLoader = require('node-haste/lib/loader/ProjectConfigurationLoader');
var ResourceMap = require('node-haste/lib/ResourceMap');
var SymbolicLinkFinder = require('./SymbolicLinkFinder');

var async = require('async');
var fs = require('fs');
var hasteLoaders = require('node-haste/lib/loaders');
var path = require('path');
var transform = require('jstransform').transform;

var reactVisitors = require('react-tools/vendor/fbtransform/visitors').getVisitorsList();
var es6Classes = require('jstransform/visitors/es6-class-visitors').visitorList;
var es6RestParameters = require('jstransform/visitors/es6-rest-param-visitors').visitorList;
var es6ArrowFunctions = require('jstransform/visitors/es6-arrow-function-visitors').visitorList;
var es6ObjectShortNotation = require('jstransform/visitors/es6-object-short-notation-visitors').visitorList;

var visitorList = reactVisitors.concat(
  es6Classes, es6RestParameters, es6ArrowFunctions, es6ObjectShortNotation
);

var originalSourceCache = {};
var transformCache = {};
var transformTimes = {};

var resourceMap = new ResourceMap();

var getResourceMapInstance = function() {
  return resourceMap;
};
var hasteInstance;

var prepareForRE = function(str) {
  return str.replace(/\./g, function() {
    return '\\.';  // escape the dots.
  });
};

/**
 * These directories show up in various places - they are never good to bundle
 * in a browser package.
 */
var BLACKLIST_EXTS = [
  '.DS_Store',
  '.git',
  '.module-cache'
].map(function(ext) {
  return prepareForRE(ext + '$');
});

/**
 * These are known to simply be tools of `react-page`, not the app that you wish
 * to package.
 */
var REACT_PAGE_DEPENDENCIES = [
  'contextify',
  'optimist',
  'connect',
  'markdown',
  'react-tools/vendor',
  'react-tools/node_modules'
];

// Everything from the react-page-middleware tree except browser-builtins.
var BLACKLIST_MIDDLEWARE_DEPENDENCIES = [
  'src',
  'polyfill',
  path.join('node_modules', 'node-terminal'),
  path.join('node_modules', 'browserify'),
  path.join('node_modules', 'react-tools'),
  path.join('node_modules', 'node-haste'),
  path.join('node_modules', 'convert-source-map'),
  path.join('node_modules', 'async'),
  path.join('node_modules', 'source-map'),
  path.join('node_modules', 'contextify'),
  path.join('node_modules', 'optimist')
];

var BLACKLIST_FILE_EXTS_RE =
  new RegExp('(' + BLACKLIST_EXTS.join('|') + ')');

var pathRegex = function(paths) {
  return new RegExp('^(' + paths.join('|') + ')');
};

var ensureBlacklistsComputed = function(buildConfig) {
  if (buildConfig._reactPageBlacklistRE && buildConfig._middlewareBlacklistRE) {
    return;
  }
  var reactPageDevDependencyPaths = REACT_PAGE_DEPENDENCIES.map(function(name) {
    return path.resolve(buildConfig.projectRoot, 'node_modules', name);
  }).map(prepareForRE);

  buildConfig._reactPageBlacklistRE = pathRegex(reactPageDevDependencyPaths);
  // Add browser-builtins to blacklist if we don't need them.
  var middlewareBlacklist = !buildConfig.useBrowserBuiltins ?
    BLACKLIST_MIDDLEWARE_DEPENDENCIES.concat('node_modules/browser-builtins/') :
    BLACKLIST_MIDDLEWARE_DEPENDENCIES;
  var middlewareBlacklistPaths = middlewareBlacklist.map(function(relPath) {
    return prepareForRE(
      path.resolve(
        buildConfig.projectRoot,
        'node_modules',
        'react-page-middleware',
        relPath
      )
    );
  });
  buildConfig._middlewareBlacklistRE = pathRegex(middlewareBlacklistPaths);
};

/**
 * The `react-page` `projectRoot` is the search root. By default, everything
 * under it is inherently whitelisted. The user may black list certain
 * directories under it. They should be careful not to blacklist the
 * `browser-builtins` in `react-page-middleware`.
 *
 * We ignore any directory that is, or is *inside* of a black listed path.
 *
 * @param {object} buildConfig React-page build config.
 * @param {string} path Absolute path in question.
 * @return {boolean} Whether or not path should be ignored.
 */
var shouldStopTraversing = function(buildConfig, path) {
  ensureBlacklistsComputed(buildConfig);
  var buildConfigBlacklistRE = buildConfig.blacklistRE;
  var internalDependenciesBlacklistRE = buildConfig._reactPageBlacklistRE;
  if (BLACKLIST_FILE_EXTS_RE.test(path) ||
      buildConfig._middlewareBlacklistRE.test(path) ||
      internalDependenciesBlacklistRE.test(path) ||
      buildConfigBlacklistRE && buildConfigBlacklistRE.test(path) ||
      buildConfig.ignorePaths && buildConfig.ignorePaths(path)) {
    return true;
  }
  return false;
};

/**
 * We must make sure to add react-page-middleware to the ignored search paths,
 * otherwise a prebuilt version of React (which still has @providesModule React)
 * could end up getting bundled with the pure version. Not only would it be
 * wasteful, but the built version requires in the form of (./Module) which
 * would fail.  We'll also filter build/modules/ which is a common place for
 * React build output.
 *
 * We pass the `projectRoot` to haste as the search directory, but we have to do
 * some really heavy filtering along the way.
 */
var getHasteInstance = function(buildConfig) {
  if (hasteInstance) {
    return hasteInstance;
  }
  var loaders = buildConfig.useBrowserBuiltins ?
    [new BrowserShimLoader(buildConfig)] : [];
  loaders = loaders.concat([
    new hasteLoaders.JSLoader({       // JS files
      extractSpecialRequires: true    // support for requireLazy, requireDynamic
    }),
    new hasteLoaders.ImageLoader({}), // Images too.
    new hasteLoaders.CSSLoader({}),   // CSS files
    new ProjectConfigurationLoader()  // package.json files
  ]);
  var ext = {};
  loaders.forEach(function(loader) {
    loader.getExtensions().forEach(function(e) {
      ext[e] = true;
    });
  });
  var finder = new SymbolicLinkFinder({
    scanDirs: [buildConfig.projectRoot],
    extensions: Object.keys(ext),
    ignore: shouldStopTraversing.bind(null, buildConfig)
  });
  return new Haste(loaders, [buildConfig.projectRoot], {finder: finder});
};

/**
 * Replaces relative modules, transforms JSX, and wraps each module in a
 * `define()`.
 */
var transformModuleImpl = function(mod, modName, rawCode, done) {
  var resolveModule = function(requiredName) {
    return mod.getModuleIDByOrigin(requiredName);
  };
  try {
    var transformResult = transform(visitorList, rawCode);
    var transformedCode = fixReactTransform(transformResult.code);
    var modularizedCode =
      Modularizer.modularize(modName, transformedCode, resolveModule);
    originalSourceCache[modName] = rawCode;
    transformCache[modName] = modularizedCode;
    transformTimes[modName] = Date.now();
    done(null, transformCache[modName]);
  } catch (e) {
    // Original error only includes esprima trace!
    var parseError =
      new Error('Syntax Error: ' + mod.path + ' ' + e.toString());
    done(parseError);
  }
};

/**
 * @param {string} str Code resulting from running transforms.
 * @return {string} Fixed string - removes additional trailing newline.
 */
var fixReactTransform = function(str) {
  return str.charAt(str.length - 1) === '\n' ?
    str.substr(0, str.length - 1) : str;
};

function warmCache(orderedModulesObj, modName, done) {
  var mod = orderedModulesObj[modName];
  if (transformTimes[modName] && transformTimes[modName] > mod.mtime) {
    done(null, transformCache[modName]);
  } else {
    fs.readFile(mod.path, {encoding: 'utf8'}, function(err, contents) {
      var error = err || (!contents ? new Error('[ERROR] no content:' +  mod.path) : null);
      if (error) {
        done(error);
      } else {
        transformModuleImpl(mod, modName, contents, done);
      }
    });
  }
}

/**
 * @param {object} Options - including callback for completion.
 */
var computePackageForAbsolutePath = function(options, onComputePackageDone) {
  /**
   * @param {string} moduleName Resolved module name, corresponding to the
   * absolute path provided as `options.rootModuleAbsolutePath`.
   * @param {Object} orderedModulesObj Key value (topologically ordered) of
   * dependent resources.
   */
  var onModuleDependenciesLoaded =
    function(err, resolvedRootModuleID, orderedModulesObj) {
      if (err) {
        return onComputePackageDone(err);
      }
      var moduleNames = Object.keys(orderedModulesObj);
      var onWarmed = function(err) {
        if (err) {
          return onComputePackageDone(err);
        }
        var ppackage = new Package();
        var modCount = 0;
        moduleNames.forEach(function(modName) {
          var mod = orderedModulesObj[modName];
          var originalSource = originalSourceCache[modName];
          var transformedSource = transformCache[modName];
          modCount = ppackage.push(mod.path, originalSource, transformedSource);
        });
        var packageErr = !modCount &&
            new Error('No modules for:' + options.rootModuleAbsolutePath);
        onComputePackageDone(packageErr, resolvedRootModuleID, ppackage);
      };
      async.each(
        moduleNames,
        warmCache.bind(null, orderedModulesObj),
        onWarmed
      );
    };

  HasteDependencyLoader.loadOrderedDependencies({
    rootDependencies: options.runtimeDependencies,
    rootJSPath: options.rootModuleAbsolutePath,
    haste: getHasteInstance(options.buildConfig),
    resourceMap: getResourceMapInstance(options.buildConfig),
    done: onModuleDependenciesLoaded,
    debug: options.buildConfig.debugPackager
  });
};

var Packager = {
  computePackageForAbsolutePath: computePackageForAbsolutePath
};

module.exports = Packager;
<commit_msg>Update reference to retrieve the jstransform visitors.<commit_after>/**
 * Copyright 2013 Facebook, Inc.
 *
 * 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.
 */
"use strict";

/**
 * Using Node-Haste.
 */
var BrowserShimLoader = require('./BrowserShimLoader');
var Haste = require('node-haste/lib/Haste');
var HasteDependencyLoader = require('node-haste/lib/HasteDependencyLoader');
var Modularizer = require('./Modularizer');
var Package = require('./Package');
var ProjectConfigurationLoader = require('node-haste/lib/loader/ProjectConfigurationLoader');
var ResourceMap = require('node-haste/lib/ResourceMap');
var SymbolicLinkFinder = require('./SymbolicLinkFinder');

var async = require('async');
var fs = require('fs');
var hasteLoaders = require('node-haste/lib/loaders');
var path = require('path');
var transform = require('jstransform').transform;

var reactVisitors = require('react-tools/vendor/fbtransform/visitors').getAllVisitors();
var es6Classes = require('jstransform/visitors/es6-class-visitors').visitorList;
var es6RestParameters = require('jstransform/visitors/es6-rest-param-visitors').visitorList;
var es6ArrowFunctions = require('jstransform/visitors/es6-arrow-function-visitors').visitorList;
var es6ObjectShortNotation = require('jstransform/visitors/es6-object-short-notation-visitors').visitorList;

var visitorList = reactVisitors.concat(
  es6Classes, es6RestParameters, es6ArrowFunctions, es6ObjectShortNotation
);

var originalSourceCache = {};
var transformCache = {};
var transformTimes = {};

var resourceMap = new ResourceMap();

var getResourceMapInstance = function() {
  return resourceMap;
};
var hasteInstance;

var prepareForRE = function(str) {
  return str.replace(/\./g, function() {
    return '\\.';  // escape the dots.
  });
};

/**
 * These directories show up in various places - they are never good to bundle
 * in a browser package.
 */
var BLACKLIST_EXTS = [
  '.DS_Store',
  '.git',
  '.module-cache'
].map(function(ext) {
  return prepareForRE(ext + '$');
});

/**
 * These are known to simply be tools of `react-page`, not the app that you wish
 * to package.
 */
var REACT_PAGE_DEPENDENCIES = [
  'contextify',
  'optimist',
  'connect',
  'markdown',
  'react-tools/vendor',
  'react-tools/node_modules'
];

// Everything from the react-page-middleware tree except browser-builtins.
var BLACKLIST_MIDDLEWARE_DEPENDENCIES = [
  'src',
  'polyfill',
  path.join('node_modules', 'node-terminal'),
  path.join('node_modules', 'browserify'),
  path.join('node_modules', 'react-tools'),
  path.join('node_modules', 'node-haste'),
  path.join('node_modules', 'convert-source-map'),
  path.join('node_modules', 'async'),
  path.join('node_modules', 'source-map'),
  path.join('node_modules', 'contextify'),
  path.join('node_modules', 'optimist')
];

var BLACKLIST_FILE_EXTS_RE =
  new RegExp('(' + BLACKLIST_EXTS.join('|') + ')');

var pathRegex = function(paths) {
  return new RegExp('^(' + paths.join('|') + ')');
};

var ensureBlacklistsComputed = function(buildConfig) {
  if (buildConfig._reactPageBlacklistRE && buildConfig._middlewareBlacklistRE) {
    return;
  }
  var reactPageDevDependencyPaths = REACT_PAGE_DEPENDENCIES.map(function(name) {
    return path.resolve(buildConfig.projectRoot, 'node_modules', name);
  }).map(prepareForRE);

  buildConfig._reactPageBlacklistRE = pathRegex(reactPageDevDependencyPaths);
  // Add browser-builtins to blacklist if we don't need them.
  var middlewareBlacklist = !buildConfig.useBrowserBuiltins ?
    BLACKLIST_MIDDLEWARE_DEPENDENCIES.concat('node_modules/browser-builtins/') :
    BLACKLIST_MIDDLEWARE_DEPENDENCIES;
  var middlewareBlacklistPaths = middlewareBlacklist.map(function(relPath) {
    return prepareForRE(
      path.resolve(
        buildConfig.projectRoot,
        'node_modules',
        'react-page-middleware',
        relPath
      )
    );
  });
  buildConfig._middlewareBlacklistRE = pathRegex(middlewareBlacklistPaths);
};

/**
 * The `react-page` `projectRoot` is the search root. By default, everything
 * under it is inherently whitelisted. The user may black list certain
 * directories under it. They should be careful not to blacklist the
 * `browser-builtins` in `react-page-middleware`.
 *
 * We ignore any directory that is, or is *inside* of a black listed path.
 *
 * @param {object} buildConfig React-page build config.
 * @param {string} path Absolute path in question.
 * @return {boolean} Whether or not path should be ignored.
 */
var shouldStopTraversing = function(buildConfig, path) {
  ensureBlacklistsComputed(buildConfig);
  var buildConfigBlacklistRE = buildConfig.blacklistRE;
  var internalDependenciesBlacklistRE = buildConfig._reactPageBlacklistRE;
  if (BLACKLIST_FILE_EXTS_RE.test(path) ||
      buildConfig._middlewareBlacklistRE.test(path) ||
      internalDependenciesBlacklistRE.test(path) ||
      buildConfigBlacklistRE && buildConfigBlacklistRE.test(path) ||
      buildConfig.ignorePaths && buildConfig.ignorePaths(path)) {
    return true;
  }
  return false;
};

/**
 * We must make sure to add react-page-middleware to the ignored search paths,
 * otherwise a prebuilt version of React (which still has @providesModule React)
 * could end up getting bundled with the pure version. Not only would it be
 * wasteful, but the built version requires in the form of (./Module) which
 * would fail.  We'll also filter build/modules/ which is a common place for
 * React build output.
 *
 * We pass the `projectRoot` to haste as the search directory, but we have to do
 * some really heavy filtering along the way.
 */
var getHasteInstance = function(buildConfig) {
  if (hasteInstance) {
    return hasteInstance;
  }
  var loaders = buildConfig.useBrowserBuiltins ?
    [new BrowserShimLoader(buildConfig)] : [];
  loaders = loaders.concat([
    new hasteLoaders.JSLoader({       // JS files
      extractSpecialRequires: true    // support for requireLazy, requireDynamic
    }),
    new hasteLoaders.ImageLoader({}), // Images too.
    new hasteLoaders.CSSLoader({}),   // CSS files
    new ProjectConfigurationLoader()  // package.json files
  ]);
  var ext = {};
  loaders.forEach(function(loader) {
    loader.getExtensions().forEach(function(e) {
      ext[e] = true;
    });
  });
  var finder = new SymbolicLinkFinder({
    scanDirs: [buildConfig.projectRoot],
    extensions: Object.keys(ext),
    ignore: shouldStopTraversing.bind(null, buildConfig)
  });
  return new Haste(loaders, [buildConfig.projectRoot], {finder: finder});
};

/**
 * Replaces relative modules, transforms JSX, and wraps each module in a
 * `define()`.
 */
var transformModuleImpl = function(mod, modName, rawCode, done) {
  var resolveModule = function(requiredName) {
    return mod.getModuleIDByOrigin(requiredName);
  };
  try {
    var transformResult = transform(visitorList, rawCode);
    var transformedCode = fixReactTransform(transformResult.code);
    var modularizedCode =
      Modularizer.modularize(modName, transformedCode, resolveModule);
    originalSourceCache[modName] = rawCode;
    transformCache[modName] = modularizedCode;
    transformTimes[modName] = Date.now();
    done(null, transformCache[modName]);
  } catch (e) {
    // Original error only includes esprima trace!
    var parseError =
      new Error('Syntax Error: ' + mod.path + ' ' + e.toString());
    done(parseError);
  }
};

/**
 * @param {string} str Code resulting from running transforms.
 * @return {string} Fixed string - removes additional trailing newline.
 */
var fixReactTransform = function(str) {
  return str.charAt(str.length - 1) === '\n' ?
    str.substr(0, str.length - 1) : str;
};

function warmCache(orderedModulesObj, modName, done) {
  var mod = orderedModulesObj[modName];
  if (transformTimes[modName] && transformTimes[modName] > mod.mtime) {
    done(null, transformCache[modName]);
  } else {
    fs.readFile(mod.path, {encoding: 'utf8'}, function(err, contents) {
      var error = err || (!contents ? new Error('[ERROR] no content:' +  mod.path) : null);
      if (error) {
        done(error);
      } else {
        transformModuleImpl(mod, modName, contents, done);
      }
    });
  }
}

/**
 * @param {object} Options - including callback for completion.
 */
var computePackageForAbsolutePath = function(options, onComputePackageDone) {
  /**
   * @param {string} moduleName Resolved module name, corresponding to the
   * absolute path provided as `options.rootModuleAbsolutePath`.
   * @param {Object} orderedModulesObj Key value (topologically ordered) of
   * dependent resources.
   */
  var onModuleDependenciesLoaded =
    function(err, resolvedRootModuleID, orderedModulesObj) {
      if (err) {
        return onComputePackageDone(err);
      }
      var moduleNames = Object.keys(orderedModulesObj);
      var onWarmed = function(err) {
        if (err) {
          return onComputePackageDone(err);
        }
        var ppackage = new Package();
        var modCount = 0;
        moduleNames.forEach(function(modName) {
          var mod = orderedModulesObj[modName];
          var originalSource = originalSourceCache[modName];
          var transformedSource = transformCache[modName];
          modCount = ppackage.push(mod.path, originalSource, transformedSource);
        });
        var packageErr = !modCount &&
            new Error('No modules for:' + options.rootModuleAbsolutePath);
        onComputePackageDone(packageErr, resolvedRootModuleID, ppackage);
      };
      async.each(
        moduleNames,
        warmCache.bind(null, orderedModulesObj),
        onWarmed
      );
    };

  HasteDependencyLoader.loadOrderedDependencies({
    rootDependencies: options.runtimeDependencies,
    rootJSPath: options.rootModuleAbsolutePath,
    haste: getHasteInstance(options.buildConfig),
    resourceMap: getResourceMapInstance(options.buildConfig),
    done: onModuleDependenciesLoaded,
    debug: options.buildConfig.debugPackager
  });
};

var Packager = {
  computePackageForAbsolutePath: computePackageForAbsolutePath
};

module.exports = Packager;
<|endoftext|>


<commit_before>
// If you use this as a template, update the copyright with your own name.

// Sample Node-RED node file

module.exports = function(RED) {
    "use strict";
    // require any external libraries we may need....
    //var foo = require("foo-library");
    // Statics go here.
    var seneca = require('seneca');
    var Promise = require('bluebird');

    // The main node definition - most things happen in here
    function SenecaNode(config) {
        // Create a RED node
        RED.nodes.createNode(this,config);

        // copy "this" object in case we need it in context of callbacks of other functions.
        var node = this;

        this.status({fill:"red",shape:"ring",text:"disconnected"});

        // Store local copies of the node configuration (as defined in the .html)
        // this.topic = config.topic;
        // Retrieve the config node
        this.connection = RED.nodes.getNode(config.connection);
        if (this.connection) {
            try {
                // Create the seneca instance here and then attach a client.
                this.connectionObject = this.connection.connection;
                this.connectionObject = JSON.parse(this.connectionObject);
                console.error("connection is ", this.connectionObject);
                console.error("type of connectionObject is ", typeof(this.connectionObject));
                var that = this; // Expose value of main context to error callback.
                this.senecaInstance = seneca(
                    {
                        errhandler: function( err ) {
                            console.log("Seneca error: ", err);
                            console.log("Config for errored service is ", that.connectionObject);
                        }
                    }
                );
                this.client = this.senecaInstance.client(this.connectionObject);
                this.senecaInstance.ready(function() {
                    node.on('input', function (msg) {
                        // msg.payload contains the command object.
                        // in this example just send it straight on... should process it here really
                        this.status({fill:"blue",shape:"dot",text:"processing"});
                        console.error("connection type = ", this.connectionObject);

                        var act = Promise.promisify(this.client.act, {context: this.client});
                        this.client.act(msg.payload)
                          .then(function (result) {
                                msg.payload.result = result;
                                node.send([null, msg]);
                                node.status({fill:"green",shape:"dot",text:"connected"});
                          })
                          .catch(function (err) {
                                node.error(err);
                                node.status({fill:"green",shape:"dot",text:"connected"});
                                node.send([err, null]);
                          });
                    });

                    node.on("close", function() {
                        // Called when the node is shutdown - eg on redeploy.
                        // Allows ports to be closed, connections dropped etc.
                        // eg: node.client.disconnect();
                        this.senecaInstance.close(function(err) {
                            node.error(err);
                        });
                    });

                    // node.on("node-status", function() {
                    // })

                    node.status({fill:"green",shape:"dot",text:"connected"});
                });
            } catch (e) {
                node.error(["Exception in seneca node", e].join(' '));
                console.error(e);
                this.status({file: "red", shape: "dot", text: "ERR: see debug panel"});
            }
        } else {
            this.status({fill:"red",shape:"dot",text:"ERR: No Config Found"});
        }

    }

    // Register the node by name. This must be called before overriding any of the
    // Node functions.
    RED.nodes.registerType("seneca",SenecaNode);

    // Register the configuration node
    function SenecaConfigNode(n) {
        RED.nodes.createNode(this,n);
        this.connection = n.connection;
        this.name = n.name;
        // console.error("Config in confignode is ", n);
    }
    RED.nodes.registerType("seneca_config",SenecaConfigNode);

};
<commit_msg>Removed promisify to debug edge cases<commit_after>
// If you use this as a template, update the copyright with your own name.

// Sample Node-RED node file

module.exports = function(RED) {
    "use strict";
    // require any external libraries we may need....
    //var foo = require("foo-library");
    // Statics go here.
    var seneca = require('seneca');
    var Promise = require('bluebird');

    // The main node definition - most things happen in here
    function SenecaNode(config) {
        // Create a RED node
        RED.nodes.createNode(this,config);

        // copy "this" object in case we need it in context of callbacks of other functions.
        var node = this;

        this.status({fill:"red",shape:"ring",text:"disconnected"});

        // Store local copies of the node configuration (as defined in the .html)
        // this.topic = config.topic;
        // Retrieve the config node
        this.connection = RED.nodes.getNode(config.connection);
        if (this.connection) {
            try {
                // Create the seneca instance here and then attach a client.
                this.connectionObject = this.connection.connection;
                this.connectionObject = JSON.parse(this.connectionObject);
                console.error("connection is ", this.connectionObject);
                console.error("type of connectionObject is ", typeof(this.connectionObject));
                var that = this; // Expose value of main context to error callback.
                this.senecaInstance = seneca(
                    {
                        errhandler: function( err ) {
                            console.log("Seneca error: ", err);
                            console.log("Config for errored service is ", that.connectionObject);
                        }
                    }
                );
                this.client = this.senecaInstance.client(this.connectionObject);
                this.senecaInstance.ready(function() {
                    node.on('input', function (msg) {
                        // msg.payload contains the command object.
                        // in this example just send it straight on... should process it here really
                        this.status({fill:"blue",shape:"dot",text:"processing"});
                        console.error("connection type = ", this.connectionObject);

                        // var act = Promise.promisify(this.client.act, {context: this.client});
                        // this.client.act(msg.payload)
                        //   .then(function (result) {
                        //         msg.payload.result = result;
                        //         node.send([null, msg]);
                        //         node.status({fill:"green",shape:"dot",text:"connected"});
                        //   })
                        //   .catch(function (err) {
                        //         node.error(err);
                        //         node.status({fill:"green",shape:"dot",text:"connected"});
                        //         node.send([err, null]);
                        //   });
                        try {
                            this.client.act(msg.payload, function(err, result) {
                                if (!err) {
                                    msg.payload.result = result;
                                    node.send([null, msg]);
                                    node.status({fill: "green", shape: "dot", text: "connected"});
                                } else {
                                    node.error(err);
                                    node.status({fill: "green", shape: "dot", text: "connected"});
                                    node.send([err, null]);
                                }
                            });
                        } catch (e) {
                            node.error(e);
                            node.status({fill: "green", shape: "dot", text: "connected"});
                            node.send([err, null]);
                        }
                    });

                    node.on("close", function() {
                        // Called when the node is shutdown - eg on redeploy.
                        // Allows ports to be closed, connections dropped etc.
                        // eg: node.client.disconnect();
                        this.senecaInstance.close(function(err) {
                            node.error(err);
                        });
                    });

                    // node.on("node-status", function() {
                    // })

                    node.status({fill:"green",shape:"dot",text:"connected"});
                });
            } catch (e) {
                node.error(["Exception in seneca node", e].join(' '));
                console.error(e);
                this.status({file: "red", shape: "dot", text: "ERR: see debug panel"});
            }
        } else {
            this.status({fill:"red",shape:"dot",text:"ERR: No Config Found"});
        }

    }

    // Register the node by name. This must be called before overriding any of the
    // Node functions.
    RED.nodes.registerType("seneca",SenecaNode);

    // Register the configuration node
    function SenecaConfigNode(n) {
        RED.nodes.createNode(this,n);
        this.connection = n.connection;
        this.name = n.name;
        // console.error("Config in confignode is ", n);
    }
    RED.nodes.registerType("seneca_config",SenecaConfigNode);

};
<|endoftext|>


<commit_before>import _ from 'lodash';
import PipeDescriptor, { isPipeDescriptor } from './PipeDescriptor';
import pipe from './pipe';
import parallel from './parallel';

export default class Pipeline {
  /**
   * @param parent {Pipeline}
   */
  constructor(parent = null) {
    // TODO - handle unexpected main and parent
    this.pipes = {
      input: [],
      main: [],
      output: [],
      catch: [],
    };

    this.last = null;
    this.parent = parent;

    this.pipe = this.pipe.bind(this);
  }

  /**
   * Add a pipe or a pipeline to the time zone.
   * When connecting pipe descriptor it will be connected to the initial type? // TODO - confirm
   * @param pipe
   * @param type
   * @returns {Pipeline}
   */
  connect(...args) {
    // TODO - optimization - Adding a pipe can automatically create new array of ordered pipes
    //  further more, main pipes can be sorted?
    const pipeDescriptor = new PipeDescriptor().create(...args);

    const { type } = pipeDescriptor;
    this.pipes[type].push(pipeDescriptor);

    this.last = pipeDescriptor;

    return this;
  }

  /**
   * Before the main pipes, used to filter out unwanted streams
   * or supply stream with data.
   * @param pipe
   * @returns {*}
   */
  input(...args) {
    return this.connect('input', ...args);
  }

  /**
   * Used to process the stream.
   * @param pipe
   * @returns {*}
   */
  main(...args) {
    return this.connect('main', ...args);
  }

  /**
   * After the stream is processed, last pipes in order.
   * @param pipe
   * @returns {*}
   */
  output(...args) {
    return this.connect('output', ...args);
  }

  catch(...args) {
    return this.connect('catch', ...args);
  }

  getInputPipes() {
    return [];
  }

  getOutputPipes() {
    return [];
  }

  replace(pipeDescriptor, newPipeDescriptor) {
    const { type } = pipeDescriptor;
    const { type: newType } = newPipeDescriptor;

    if (type !== newType) {
      throw Error(`Trying to replace ${type} with ${newType}`);
    }

    const pipes = this.pipes[type];
    const index = _.indexOf(pipes, pipeDescriptor);

    pipes[index] = newPipeDescriptor;
  }

  /**
   *
   * @param enhancer {function}
   * @returns {Pipeline}
   */
  enhance(enhancer) {
    const { last: lastPipe } = this;
    const pipe = enhancer(lastPipe.pipe);
    const enhancedPipe = lastPipe.replicate({ pipe });
    this.replace(lastPipe, enhancedPipe);
    return this;
  }

  /**
   * Either return new pipeline reference or create new from pipe.
   * @returns {*}
   */
  take() {
    // TODO - is it clear that "take" returns only last pipeline which doesn't have previous pipelines?
    const pipeDescriptor = this.last;

    if (!pipeDescriptor) {
      throw Error('Trying to take the last pipe in an empty Pipeline');
    }

    const pipeline =  new Pipeline(this).connect(...pipeDescriptor.args({ type: 'main' }));

    // TODO - Add tests
    const newPipeDescriptor = new PipeDescriptor().create(pipeDescriptor.type, pipeline);
    this.replace(pipeDescriptor, newPipeDescriptor);
    // TODO - optimization - add to the last.meta "replicated" flag so that it is safe to mutate it.
    // Is "replicated" flag secure enough that it can be mutate? What are the cases new copy is needed?
    this.last = newPipeDescriptor;

    // TODO - rethink disconnect/remove binding
    pipeline.remove = () => {
      // Removing a pipe does not effect this.pipes.last because
      // last is used only to create snapshot
      _.remove(this.pipes[pipeDescriptor.type], newPipeDescriptor);
    };

    return pipeline;
  }

  /**
   * Add a pipe to the last pipe in the chain of the last pipe -_-
   * Recursively find the last pipe in the chain which doesn't have last pipe.
   * Because pipes in the same sector may not depended on each other we can relay to chain
   * related pipes to the output.
   * @param pipe
   * @returns {Pipeline}
   */
  chain(...args) {
    const last = isPipeDescriptor(_.tail(args)) ? args.pop() : this.take();
    const lastOutputs = last.pipes.output;
    if (_.isEmpty(lastOutputs)) {
      last.output(...args);
    } else {
      // "take()" is needed to avoid mutating existing pipeline
      const lastOutput = _.tail(lastOutputs).take();
      last.chain(...args, lastOutput);
    }
    return this;
  }

  return() { // TODO - confirm name; parent?
    if (!this.parent) {
      throw Error(
        'Returning to the unexisting parent pipeline. ' +
        'Use the `return` only when a pipeline has the parent. ' +
        'Common case for using the `return` is to get back to parent ' +
        'pipeline after a child has been taken with the `take`.'
      );
    }
    return this.parent;
  }

  /**
   * Deep copy.
   * Create the new Pipeline that recreates all pipes as current.
   * All references are changed, there is no relation between
   * the new Pipeline and the current.
   */
  replicate() {
    const pipeline = new Pipeline();

    // TODO - Rethink pipes inheritance (this particular set bellow)
    pipeline.pipes = this.replicatePipes();

    return pipeline;
  }

  replicatePipes() {
    return _.reduce(this.pipes, (pipesCopy, pipes, name) => {
      pipesCopy[name] = replicatePipes(pipes);
      return pipesCopy;
    }, {});
  }

  compose() {
    const { input, main, output } = this.pipes;
    return [input, ...this.getInputPipes(), main, ...this.getOutputPipes(), output];
  }

  /**
   * Pipeline is serial if it has parent and whenever it is connected to another pipeline.
   * It can explicitly be disconnected (connected in parallel) for certain fitting (connection).
   * @param stream
   * @returns {Promise}
   */
  pipe(stream = {}) {
    const promise = new Promise((resolve, reject) => {
      const catchPipeline = errorStream => {
        // Closing catch error will end catch cycle
        parallel(errorStream, this.pipes.catch).then(reject).catch(reject);
      };

      const sections = this.compose();

      _.reduce(sections, (nextPromise, section) => {
        return nextPromise.then((stream) => parallel(stream, section));
      }, Promise.resolve(stream))
        .then(resolve)
        .catch(catchPipeline);
    });

    // Primary added to remove unhandled promise warning.
    // Catch is handled before, this is just a helper promise.
    // TODO - find good way to handle this
    promise.catch(() => {});

    return promise;
  }
}

export function replicatePipes(pipes) {
  return _.map(pipes, replicatePipe);
}

export function replicatePipe(pipe) {
  if (isPipeline(pipe)) {
    return pipe.replicate();
  } else if (isPipeDescriptor(pipe)) {
    return pipe.replicate();
  } else if (_.isFunction(pipe)) {
    return pipe;
  } else if (_.isNull(pipe)) {
    return pipe;
  }
  throw Error(`Trying to replicate an invalid pipe: ${pipe}`);
}

export const isPipeline = ref => ref instanceof Pipeline;

/**
 * Inverse always call pipe as serial.
 * It doesn't have sense to use inverse in case something is connected in parallel.
 * Parallel pipes doesn't affect original stream.
 * @param pipe
 * @returns {Function}
 */
export const inverse = nextPipe => stream =>
  new Promise((resolve, reject) => {
    pipe(stream, nextPipe).then(reject).catch(resolve)
  });
<commit_msg>Implement Pipeline.reconcileStreams<commit_after>import _ from 'lodash';
import PipeDescriptor, { isPipeDescriptor } from './PipeDescriptor';
import pipe from './pipe';
import parallel from './parallel';

export default class Pipeline {
  /**
   * @param parent {Pipeline}
   */
  constructor(parent = null) {
    // TODO - handle unexpected main and parent
    this.pipes = {
      input: [],
      main: [],
      output: [],
      catch: [],
    };

    this.last = null;
    this.parent = parent;

    this.pipe = this.pipe.bind(this);
  }

  /**
   * Add a pipe or a pipeline to the time zone.
   * When connecting pipe descriptor it will be connected to the initial type? // TODO - confirm
   * @param pipe
   * @param type
   * @returns {Pipeline}
   */
  connect(...args) {
    // TODO - optimization - Adding a pipe can automatically create new array of ordered pipes
    //  further more, main pipes can be sorted?
    const pipeDescriptor = new PipeDescriptor().create(...args);

    const { type } = pipeDescriptor;
    this.pipes[type].push(pipeDescriptor);

    this.last = pipeDescriptor;

    return this;
  }

  /**
   * Before the main pipes, used to filter out unwanted streams
   * or supply stream with data.
   * @param pipe
   * @returns {*}
   */
  input(...args) {
    return this.connect('input', ...args);
  }

  /**
   * Used to process the stream.
   * @param pipe
   * @returns {*}
   */
  main(...args) {
    return this.connect('main', ...args);
  }

  /**
   * After the stream is processed, last pipes in order.
   * @param pipe
   * @returns {*}
   */
  output(...args) {
    return this.connect('output', ...args);
  }

  catch(...args) {
    return this.connect('catch', ...args);
  }

  getInputPipes() {
    return [];
  }

  getOutputPipes() {
    return [];
  }

  replace(pipeDescriptor, newPipeDescriptor) {
    const { type } = pipeDescriptor;
    const { type: newType } = newPipeDescriptor;

    if (type !== newType) {
      throw Error(`Trying to replace ${type} with ${newType}`);
    }

    const pipes = this.pipes[type];
    const index = _.indexOf(pipes, pipeDescriptor);

    pipes[index] = newPipeDescriptor;
  }

  /**
   *
   * @param enhancer {function}
   * @returns {Pipeline}
   */
  enhance(enhancer) {
    const { last: lastPipe } = this;
    const pipe = enhancer(lastPipe.pipe);
    const enhancedPipe = lastPipe.replicate({ pipe });
    this.replace(lastPipe, enhancedPipe);
    return this;
  }

  /**
   * Either return new pipeline reference or create new from pipe.
   * @returns {*}
   */
  take() {
    // TODO - is it clear that "take" returns only last pipeline which doesn't have previous pipelines?
    const pipeDescriptor = this.last;

    if (!pipeDescriptor) {
      throw Error('Trying to take the last pipe in an empty Pipeline');
    }

    const pipeline =  new Pipeline(this).connect(...pipeDescriptor.args({ type: 'main' }));

    // TODO - Add tests
    const newPipeDescriptor = new PipeDescriptor().create(pipeDescriptor.type, pipeline);
    this.replace(pipeDescriptor, newPipeDescriptor);
    // TODO - optimization - add to the last.meta "replicated" flag so that it is safe to mutate it.
    // Is "replicated" flag secure enough that it can be mutate? What are the cases new copy is needed?
    this.last = newPipeDescriptor;

    // TODO - rethink disconnect/remove binding
    pipeline.remove = () => {
      // Removing a pipe does not effect this.pipes.last because
      // last is used only to create snapshot
      _.remove(this.pipes[pipeDescriptor.type], newPipeDescriptor);
    };

    return pipeline;
  }

  /**
   * Add a pipe to the last pipe in the chain of the last pipe -_-
   * Recursively find the last pipe in the chain which doesn't have last pipe.
   * Because pipes in the same sector may not depended on each other we can relay to chain
   * related pipes to the output.
   * @param pipe
   * @returns {Pipeline}
   */
  chain(...args) {
    const last = isPipeDescriptor(_.tail(args)) ? args.pop() : this.take();
    const lastOutputs = last.pipes.output;
    if (_.isEmpty(lastOutputs)) {
      last.output(...args);
    } else {
      // "take()" is needed to avoid mutating existing pipeline
      const lastOutput = _.tail(lastOutputs).take();
      last.chain(...args, lastOutput);
    }
    return this;
  }

  return() { // TODO - confirm name; parent?
    if (!this.parent) {
      throw Error(
        'Returning to the unexisting parent pipeline. ' +
        'Use the `return` only when a pipeline has the parent. ' +
        'Common case for using the `return` is to get back to parent ' +
        'pipeline after a child has been taken with the `take`.'
      );
    }
    return this.parent;
  }

  /**
   * Deep copy.
   * Create the new Pipeline that recreates all pipes as current.
   * All references are changed, there is no relation between
   * the new Pipeline and the current.
   */
  replicate() {
    const pipeline = new Pipeline();

    // TODO - Rethink pipes inheritance (this particular set bellow)
    pipeline.pipes = this.replicatePipes();

    return pipeline;
  }

  replicatePipes() {
    return _.reduce(this.pipes, (pipesCopy, pipes, name) => {
      pipesCopy[name] = replicatePipes(pipes);
      return pipesCopy;
    }, {});
  }

  compose() {
    const { input, main, output } = this.pipes;
    return [input, ...this.getInputPipes(), main, ...this.getOutputPipes(), output];
  }

  reconcileStreams(stream, resolvedStreams) {
    return _.isObject(stream) ? _.assign({}, ...resolvedStreams) : _.last(resolvedStreams);
  }

  /**
   * Pipeline is serial if it has parent and whenever it is connected to another pipeline.
   * It can explicitly be disconnected (connected in parallel) for certain fitting (connection).
   * @param stream
   * @returns {Promise}
   */
  pipe(stream = {}) {
    const promise = new Promise((resolve, reject) => {
      const catchPipeline = errorStream => {
        // Closing catch error will end catch cycle
        parallel(errorStream, this.pipes.catch, this.reconcileStreams)
          .then(reject)
          .catch(reject);
      };

      const sections = this.compose();

      _.reduce(sections, (nextPromise, section) => {
        return nextPromise.then((stream) => parallel(stream, section, this.reconcileStreams));
      }, Promise.resolve(stream))
        .then(resolve)
        .catch(catchPipeline);
    });

    // Primary added to remove unhandled promise warning.
    // Catch is handled before, this is just a helper promise.
    // TODO - find good way to handle this
    promise.catch(() => {});

    return promise;
  }
}

export function replicatePipes(pipes) {
  return _.map(pipes, replicatePipe);
}

export function replicatePipe(pipe) {
  if (isPipeline(pipe)) {
    return pipe.replicate();
  } else if (isPipeDescriptor(pipe)) {
    return pipe.replicate();
  } else if (_.isFunction(pipe)) {
    return pipe;
  } else if (_.isNull(pipe)) {
    return pipe;
  }
  throw Error(`Trying to replicate an invalid pipe: ${pipe}`);
}

export const isPipeline = ref => ref instanceof Pipeline;

/**
 * Inverse always call pipe as serial.
 * It doesn't have sense to use inverse in case something is connected in parallel.
 * Parallel pipes doesn't affect original stream.
 * @param pipe
 * @returns {Function}
 */
export const inverse = nextPipe => stream =>
  new Promise((resolve, reject) => {
    pipe(stream, nextPipe).then(reject).catch(resolve)
  });
<|endoftext|>