<commit_before>var $ = require('jquery');
var WindowStorage = require('../utils/windowStorage');

var RelatedPopups = function() {
    this.windowStorage = new WindowStorage('relatedWindows');
};

RelatedPopups.prototype = {
    updateLinks: function($select) {
        $select.find('~ .change-related, ~ .delete-related, ~ .add-another').each(function() {
            var $link = $(this);
            var hrefTemplate = $link.data('href-template');
            var value = $select.val();

            if (hrefTemplate == undefined) {
                return;
            }

            if (value) {
                $link.attr('href', hrefTemplate.replace('__fk__', value))
            } else {
                $link.removeAttr('href');
            }
        });
    },
    initLinks: function() {
        var obj = this;

        $('.form-row select').each(function() {
            var $select = $(this);

            obj.updateLinks($select);

            $select.find('~ .add-related, ~ .change-related, ~ .delete-related, ~ .add-another').each(function() {
                var $link = $(this);

                $link.on('click', function(e) {
                    e.preventDefault();

                    var href = $link.attr('href');

                    if (href != undefined) {
                        if (href.indexOf('_popup') == -1) {
                            href += (href.indexOf('?') == -1) ? '?_popup=1' : '&_popup=1';
                        }

                        obj.showPopup($select, href);
                    }
                });
            });
        }).on('change', function() {
            obj.updateLinks($(this));
        });

        $('.form-row input').each(function() {
            var $input = $(this);

            $input.find('~ .related-lookup').each(function() {
                var $link = $(this);

                $link.on('click', function(e) {
                    e.preventDefault();

                    var href = $link.attr('href');

                    href += (href.indexOf('?') == -1) ? '?_popup=1' : '&_popup=1';

                    obj.showPopup($input, href);
                });
            });
        });
    },
    initPopupBackButton: function() {
        var obj = this;

        $('.related-popup-back').on('click', function(e) {
            e.preventDefault();
            obj.closePopup();
        });
    },
    showPopup: function($input, href) {
        var $document = $(window.top.document);
        var $container = $document.find('.related-popup-container');
        var $loading = $container.find('.loading-indicator');
        var $body = $document.find('body').addClass('non-scrollable');
        var $popup = $('<iframe>')
            .attr('src', href)
            .data('input', $input)
            .addClass('related-popup')
            .on('load', function() {
                $popup.add($document.find('.related-popup-back')).fadeIn(200, 'swing', function() {
                    $loading.hide();
                });
            });

        $loading.show();
        $container.fadeIn(200, 'swing', function() {
            $container.append($popup);
        });
        $body.addClass('non-scrollable');
    },
    closePopup: function(response) {
        var previousWindow = this.windowStorage.previous();
        var obj = this;

        (function($) {
            var $document = $(window.top.document);
            var $popups = $document.find('.related-popup');
            var $container = $document.find('.related-popup-container');
            var $popup = $popups.last();

            if (response != undefined) {
                var $input = $popup.data('input');

                switch (response.action) {
                    case 'change':
                        $input.find('option').each(function() {
                            var $option = $(this);

                            if ($option.val() == response.value) {
                                $option.html(response.obj).val(response.new_value);
                            }
                        });

                        $input.trigger('change').trigger('select:init');

                        break;
                    case 'delete':
                        $input.find('option').each(function() {
                            var $option = $(this);

                            if ($option.val() == response.value) {
                                $option.remove();
                            }
                        });

                        $input.trigger('change').trigger('select:init');

                        break;
                    default:
                        if ($input.is('select')) {
                            var $option = $('<option>')
                                .val(response.value)
                                .html(response.obj);

                            $input.append($option);
                            $option.attr('selected', true);

                            $input
                                .trigger('change')
                                .trigger('select:init');
                        } else if ($input.is('input.vManyToManyRawIdAdminField') && $input.val()) {
                            $input.val($input.val() + ',' + response.value);
                        } else if ($input.is('input')) {
                            $input.val(response.value);
                        }

                        break;
                }
            }

            obj.windowStorage.pop();

            if ($popups.length == 1) {
                $container.fadeOut(200, 'swing', function() {
                    $document.find('.related-popup-back').hide();
                    $document.find('body').removeClass('non-scrollable');
                    $popup.remove();
                });
            } else {
                $popup.remove();
            }
        })(previousWindow ? previousWindow.jet.jQuery : $);
    },
    processPopupResponse: function() {
        var obj = this;

        $('#django-admin-popup-response-constants').each(function() {
            var $constants = $(this);
            var response = $constants.data('popup-response');

            obj.closePopup(response);
        });
    },
    overrideRelatedGlobals: function() {
        var obj = this;

        window.showRelatedObjectLookupPopup
            = window.showAddAnotherPopup
            = window.showRelatedObjectPopup
            = function() { };

        window.opener = this.windowStorage.previous();
        window.dismissRelatedLookupPopup = function(win, chosenId) {
            obj.closePopup({
                action: 'lookup',
                value: chosenId
            });
        };
    },
    initDeleteRelatedCancellation: function() {
        var obj = this;

        $('.popup.delete-confirmation .cancel-link').on('click', function(e) {
            e.preventDefault();
            obj.closePopup();
        }).removeAttr('onclick');
    },
    run: function() {
        this.windowStorage.push(window);

        try {
            this.initLinks();
            this.initPopupBackButton();
            this.processPopupResponse();
            this.overrideRelatedGlobals();
            this.initDeleteRelatedCancellation();
            this.overrideRelatedGlobals();
        } catch (e) {
            console.error(e);
        }
    }
};

$(document).ready(function() {
    new RelatedPopups().run();
});
<commit_msg>Refactor<commit_after>var $ = require('jquery');
var WindowStorage = require('../utils/windowStorage');

var RelatedPopups = function() {
    this.windowStorage = new WindowStorage('relatedWindows');
};

RelatedPopups.prototype = {
    updateLinks: function($select) {
        $select.find('~ .change-related, ~ .delete-related, ~ .add-another').each(function() {
            var $link = $(this);
            var hrefTemplate = $link.data('href-template');

            if (hrefTemplate == undefined) {
                return;
            }

            var value = $select.val();

            if (value) {
                $link.attr('href', hrefTemplate.replace('__fk__', value))
            } else {
                $link.removeAttr('href');
            }
        });
    },
    initLinks: function() {
        var obj = this;

        $('.form-row select').each(function() {
            var $select = $(this);

            obj.updateLinks($select);

            $select.find('~ .add-related, ~ .change-related, ~ .delete-related, ~ .add-another').each(function() {
                var $link = $(this);

                $link.on('click', function(e) {
                    e.preventDefault();

                    var href = $link.attr('href');

                    if (href != undefined) {
                        if (href.indexOf('_popup') == -1) {
                            href += (href.indexOf('?') == -1) ? '?_popup=1' : '&_popup=1';
                        }

                        obj.showPopup($select, href);
                    }
                });
            });
        }).on('change', function() {
            obj.updateLinks($(this));
        });

        $('.form-row input').each(function() {
            var $input = $(this);

            $input.find('~ .related-lookup').each(function() {
                var $link = $(this);

                $link.on('click', function(e) {
                    e.preventDefault();

                    var href = $link.attr('href');

                    href += (href.indexOf('?') == -1) ? '?_popup=1' : '&_popup=1';

                    obj.showPopup($input, href);
                });
            });
        });
    },
    initPopupBackButton: function() {
        var obj = this;

        $('.related-popup-back').on('click', function(e) {
            e.preventDefault();
            obj.closePopup();
        });
    },
    showPopup: function($input, href) {
        var $document = $(window.top.document);
        var $container = $document.find('.related-popup-container');
        var $loading = $container.find('.loading-indicator');
        var $body = $document.find('body').addClass('non-scrollable');
        var $popup = $('<iframe>')
            .attr('src', href)
            .data('input', $input)
            .addClass('related-popup')
            .on('load', function() {
                $popup.add($document.find('.related-popup-back')).fadeIn(200, 'swing', function() {
                    $loading.hide();
                });
            });

        $loading.show();
        $container.fadeIn(200, 'swing', function() {
            $container.append($popup);
        });
        $body.addClass('non-scrollable');
    },
    closePopup: function(response) {
        var previousWindow = this.windowStorage.previous();
        var obj = this;

        (function($) {
            var $document = $(window.top.document);
            var $popups = $document.find('.related-popup');
            var $container = $document.find('.related-popup-container');
            var $popup = $popups.last();

            if (response != undefined) {
                var $input = $popup.data('input');

                switch (response.action) {
                    case 'change':
                        $input.find('option').each(function() {
                            var $option = $(this);

                            if ($option.val() == response.value) {
                                $option.html(response.obj).val(response.new_value);
                            }
                        });

                        $input.trigger('change').trigger('select:init');

                        break;
                    case 'delete':
                        $input.find('option').each(function() {
                            var $option = $(this);

                            if ($option.val() == response.value) {
                                $option.remove();
                            }
                        });

                        $input.trigger('change').trigger('select:init');

                        break;
                    default:
                        if ($input.is('select')) {
                            var $option = $('<option>')
                                .val(response.value)
                                .html(response.obj);

                            $input.append($option);
                            $option.attr('selected', true);

                            $input
                                .trigger('change')
                                .trigger('select:init');
                        } else if ($input.is('input.vManyToManyRawIdAdminField') && $input.val()) {
                            $input.val($input.val() + ',' + response.value);
                        } else if ($input.is('input')) {
                            $input.val(response.value);
                        }

                        break;
                }
            }

            obj.windowStorage.pop();

            if ($popups.length == 1) {
                $container.fadeOut(200, 'swing', function() {
                    $document.find('.related-popup-back').hide();
                    $document.find('body').removeClass('non-scrollable');
                    $popup.remove();
                });
            } else {
                $popup.remove();
            }
        })(previousWindow ? previousWindow.jet.jQuery : $);
    },
    processPopupResponse: function() {
        var obj = this;

        $('#django-admin-popup-response-constants').each(function() {
            var $constants = $(this);
            var response = $constants.data('popup-response');

            obj.closePopup(response);
        });
    },
    overrideRelatedGlobals: function() {
        var obj = this;

        window.showRelatedObjectLookupPopup
            = window.showAddAnotherPopup
            = window.showRelatedObjectPopup
            = function() { };

        window.opener = this.windowStorage.previous();
        window.dismissRelatedLookupPopup = function(win, chosenId) {
            obj.closePopup({
                action: 'lookup',
                value: chosenId
            });
        };
    },
    initDeleteRelatedCancellation: function() {
        var obj = this;

        $('.popup.delete-confirmation .cancel-link').on('click', function(e) {
            e.preventDefault();
            obj.closePopup();
        }).removeAttr('onclick');
    },
    run: function() {
        this.windowStorage.push(window);

        try {
            this.initLinks();
            this.initPopupBackButton();
            this.processPopupResponse();
            this.overrideRelatedGlobals();
            this.initDeleteRelatedCancellation();
            this.overrideRelatedGlobals();
        } catch (e) {
            console.error(e);
        }
    }
};

$(document).ready(function() {
    new RelatedPopups().run();
});
<|endoftext|>


<commit_before>/**
 * @file Portal component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {createPortal} from 'react-dom';
import classNames from 'classnames';
import addClass from 'dom-helpers/class/addClass';
import removeClass from 'dom-helpers/class/removeClass';
import portalManagement from 'src/_vendors/PortalManagement'

class Portal extends Component {

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.wrapper = null;
        this.portal = null;

    }

    renderWrapper = () => {

        if (this.wrapper) {
            return this.wrapper;
        }

        const {

            className, parentEl,

            // not passing down these props
            children, visible,

            ...restProps

        } = this.props;

        if (!parentEl) {
            return null;
        }

        // addClass(parentEl, 'portal-parent');

        this.wrapper = document.createElement('div');
        this.wrapper.className = classNames('portal', {
            [className]: className
        });

        for (let key in restProps) {
            this.wrapper[key] = restProps[key];
        }

        portalManagement.push(parentEl, this.wrapper);

        return parentEl.appendChild(this.wrapper);

    };

    renderPortal = () => {

        if (!this.renderWrapper()) {
            return null;
        }

        return this.portal = createPortal(
            this.props.children,
            this.wrapper
        );

    };

    unmountWrapper = () => {

        const {parentEl} = this.props;

        if (this.wrapper && parentEl) {
            parentEl.removeChild(this.wrapper);
            portalManagement.pop(parentEl, this.wrapper);
            // removeClass(parentEl, 'portal-parent');
            this.wrapper = null;
        }

    };

    componentWillReceiveProps(nextProps) {
        if (!nextProps.visible) {
            this.unmountWrapper();
        }

    }

    componentWillUnmount() {
        this.unmountWrapper();
    }

    render() {
        return this.props.visible ? this.renderPortal() : null;
    }

}

Portal.propTypes = {
    visible: PropTypes.bool,
    parentEl: PropTypes.object
};

Portal.defaultProps = {
    visible: false,
    parentEl: document.body
};

export default Portal;
<commit_msg>update Portal<commit_after>/**
 * @file Portal component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {createPortal} from 'react-dom';
import classNames from 'classnames';
import portalManagement from '../_vendors/PortalManagement';

class Portal extends Component {

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.wrapper = null;
        this.portal = null;

    }

    renderWrapper = () => {

        if (this.wrapper) {
            return this.wrapper;
        }

        const {

            className, parentEl,

            // not passing down these props
            children, visible,

            ...restProps

        } = this.props;

        if (!parentEl) {
            return null;
        }

        // addClass(parentEl, 'portal-parent');

        this.wrapper = document.createElement('div');
        this.wrapper.className = classNames('portal', {
            [className]: className
        });

        for (let key in restProps) {
            this.wrapper[key] = restProps[key];
        }

        portalManagement.push(parentEl, this.wrapper);

        return parentEl.appendChild(this.wrapper);

    };

    renderPortal = () => {

        if (!this.renderWrapper()) {
            return null;
        }

        return this.portal = createPortal(
            this.props.children,
            this.wrapper
        );

    };

    unmountWrapper = () => {

        const {parentEl} = this.props;

        if (this.wrapper && parentEl) {
            parentEl.removeChild(this.wrapper);
            portalManagement.pop(parentEl, this.wrapper);
            // removeClass(parentEl, 'portal-parent');
            this.wrapper = null;
        }

    };

    componentWillReceiveProps(nextProps) {
        if (!nextProps.visible) {
            this.unmountWrapper();
        }

    }

    componentWillUnmount() {
        this.unmountWrapper();
    }

    render() {
        return this.props.visible ? this.renderPortal() : null;
    }

}

Portal.propTypes = {
    visible: PropTypes.bool,
    parentEl: PropTypes.object
};

Portal.defaultProps = {
    visible: false,
    parentEl: document.body
};

export default Portal;
<|endoftext|>


<commit_before>var _       = require("underscore")._,
    async   = require("async"),
    fs      = require("fs"),
    exec    = require("child_process").exec,
    iwlist     = require("./iwlist"),
    config  = require("../config.json");
var scanResult;

// Better template format
_.templateSettings = {
    interpolate: /\{\{(.+?)\}\}/g,
    evaluate :   /\{\[([\s\S]+?)\]\}/g
};

// Helper function to write a given template to a file based on a given
// context
function write_template_to_file(template_path, file_name, context, callback) {
    async.waterfall([

        function read_template_file(next_step) {
            fs.readFile(template_path, {encoding: "utf8"}, next_step);
        },

        function update_file(file_txt, next_step) {
            var template = _.template(file_txt);
            fs.writeFile(file_name, template(context), next_step);
        }

    ], callback);
}

/*****************************************************************************\
    Return a set of functions which we can use to manage and check our wifi
    connection information
\*****************************************************************************/
module.exports = function() {
    // Detect which wifi driver we should use, the rtl871xdrv or the nl80211
    exec("iw list", function(error, stdout, stderr) {
        if (stderr.match(/^nl80211 not found/)) {
            config.wifi_driver_type = "rtl871xdrv";
        }
        // console.log("config.wifi_driver_type = " + config.wifi_driver_type);
    });

    // Hack: this just assumes that the outbound interface will be "wlan0"

    // Define some globals
    var ifconfig_fields = {
        "hw_addr":         /HWaddr\s([^\s]+)/,
        "inet_addr":       /inet addr:([^\s]+)/,
    },  iwconfig_fields = {
        "ap_addr":         /Access Point:\s([^\s]+)/,
        "ap_ssid":         /ESSID:\"([^\"]+)\"/,
        "unassociated":    /(unassociated)\s+Nick/,
    },  last_wifi_info = null;

    // TODO: rpi-config-ap hardcoded, should derive from a constant

    // Get generic info on an interface
    var _get_wifi_info = function(callback) {
        var output = {
            hw_addr:      "<unknown>",
            inet_addr:    "<unknown>",
            unassociated: "<unknown>",
        };

        // Inner function which runs a given command and sets a bunch
        // of fields
        function run_command_and_set_fields(cmd, fields, callback) {
            exec(cmd, function(error, stdout, stderr) {
                if (error) return callback(error);
                for (var key in fields) {
                    re = stdout.match(fields[key]);
                    if (re && re.length > 1) {
                        output[key] = re[1];
                    }
                }
                callback(null);
            });
        }

        // Run a bunch of commands and aggregate info
        async.series([
            function run_ifconfig(next_step) {
                run_command_and_set_fields("ifconfig wlan0", ifconfig_fields, next_step);
            },
            function run_iwconfig(next_step) {
                run_command_and_set_fields("iwconfig wlan0", iwconfig_fields, next_step);
            },
        ], function(error) {
            last_wifi_info = output;
            return callback(error, output);
        });
    },

    _reboot_wireless_network = function(wlan_iface, callback) {
        async.series([
            function down(next_step) {
                exec("sudo ifdown " + wlan_iface, function(error, stdout, stderr) {
                    if (!error) console.log("ifdown " + wlan_iface + " successful...");
                    next_step();
                });
            },
            function up(next_step) {
                exec("sudo ifup " + wlan_iface, function(error, stdout, stderr) {
                    if (!error) console.log("ifup " + wlan_iface + " successful...");
                    next_step();
                });
            },
        ], callback);
    },

    // Wifi related functions
    _is_wifi_enabled_sync = function(info) {
        // If we are not an AP, and we have a valid
        // inet_addr - wifi is enabled!
        if (null        == _is_ap_enabled_sync(info) &&
            "<unknown>" != info["inet_addr"]         &&
            "<unknown>" == info["unassociated"] ) {
            return info["inet_addr"];
        }
        return null;
    },

    _is_wifi_enabled = function(callback) {
        _get_wifi_info(function(error, info) {
            if (error) return callback(error, null);
            return callback(null, _is_wifi_enabled_sync(info));
        });
    },

    // Access Point related functions
    _is_ap_enabled_sync = function(info) {
        // If the hw_addr matches the ap_addr
        // and the ap_ssid matches "rpi-config-ap"
        // then we are in AP mode
        var is_ap  =
            info["hw_addr"].toLowerCase() == info["ap_addr"].toLowerCase() &&
            info["ap_ssid"] == config.access_point.ssid;
        return (is_ap) ? info["hw_addr"].toLowerCase() : null;
    },

    _is_ap_enabled = function(callback) {
        _get_wifi_info(function(error, info) {
            if (error) return callback(error, null);
            return callback(null, _is_ap_enabled_sync(info));
        });
    },

    // Enables the accesspoint w/ bcast_ssid. This assumes that both
    // isc-dhcp-server and hostapd are installed using:
    // $sudo npm run-script provision
    _enable_ap_mode = function(bcast_ssid, callback) {
        _is_ap_enabled(function(error, result_addr) {
            if (error) {
                console.log("ERROR: " + error);
                return callback(error);
            }

            if (result_addr && !config.access_point.force_reconfigure) {
                console.log("\nAccess point is enabled with ADDR: " + result_addr);
                return callback(null);
            } else if (config.access_point.force_reconfigure) {
                console.log("\nForce reconfigure enabled - reset AP");
            } else {
                console.log("\nAP is not enabled yet... enabling...");
            }

            var context = config.access_point;
            context["enable_ap"] = true;
            context["wifi_driver_type"] = config.wifi_driver_type;

            // Here we need to actually follow the steps to enable the ap
            async.series([

                // Enable the access point ip and netmask + static
                // DHCP for the wlan0 interface
                function update_interfaces(next_step) {
                    write_template_to_file(
                        "./assets/etc/network/interfaces.ap.template",
                        "/etc/network/interfaces",
                        context, next_step);
                },

                // Enable DHCP conf, set authoritative mode and subnet
                function update_dhcpd(next_step) {
                    var context = config.access_point;
                    // We must enable this to turn on the access point
                    write_template_to_file(
                        "./assets/etc/dhcp/dhcpd.conf.template",
                        "/etc/dhcp/dhcpd.conf",
                        context, next_step);
                },

                // Enable the interface in the dhcp server
                function update_dhcp_interface(next_step) {
                    write_template_to_file(
                        "./assets/etc/default/isc-dhcp-server.template",
                        "/etc/default/isc-dhcp-server",
                        context, next_step);
                },

                // Enable hostapd.conf file
                function update_hostapd_conf(next_step) {
                    write_template_to_file(
                        "./assets/etc/hostapd/hostapd.conf.template",
                        "/etc/hostapd/hostapd.conf",
                        context, next_step);
                },

                function update_hostapd_default(next_step) {
                    write_template_to_file(
                        "./assets/etc/default/hostapd.template",
                        "/etc/default/hostapd",
                        context, next_step);
                },
                function scan_networks(next_step){
		_reboot_wireless_network(context.wifi_interface,function(){
                  iwlist(function(error,result){
                    scanResult = result;
                    console.log(JSON.stringify(result));
			next_step();
                  });
		});
                },

                function reboot_network_interfaces(next_step) {
                    _reboot_wireless_network(context.wifi_interface, next_step);
                },

                function restart_dhcp_service(next_step) {
                    exec("service isc-dhcp-server restart", function(error, stdout, stderr) {
                        //console.log(stdout);
                        if (!error) console.log("... dhcp server restarted!");
                        next_step();
                    });
                },

                function restart_hostapd_service(next_step) {
                    exec("service hostapd restart", function(error, stdout, stderr) {
                        //console.log(stdout);
                        if (!error) console.log("... hostapd restarted!");
                        next_step();
                    });
                },

                // TODO: Do we need to issue a reboot here?

            ], callback(scanResult);
        });
    },

    // Disables AP mode and reverts to wifi connection
    _enable_wifi_mode = function(connection_info, callback) {

        _is_wifi_enabled(function(error, result_ip) {
            if (error) return callback(error);

            if (result_ip) {
                console.log("\nWifi connection is enabled with IP: " + result_ip);
                return callback(null);
            }

            async.series([

                // Update /etc/network/interface with correct info...
                function update_interfaces(next_step) {
                    write_template_to_file(
                        "./assets/etc/network/interfaces.wifi.template",
                        "/etc/network/interfaces",
                        connection_info, next_step);
                },

                // Stop the DHCP server...
                function restart_dhcp_service(next_step) {
                    exec("service isc-dhcp-server stop", function(error, stdout, stderr) {
                        //console.log(stdout);
                        if (!error) console.log("... dhcp server stopped!");
                        next_step();
                    });
                },

                function reboot_network_interfaces(next_step) {
                    _reboot_wireless_network(config.wifi_interface, next_step);
                },

            ], callback);
        });

    };

    return {
        get_wifi_info:           _get_wifi_info,
        reboot_wireless_network: _reboot_wireless_network,

        is_wifi_enabled:         _is_wifi_enabled,
        is_wifi_enabled_sync:    _is_wifi_enabled_sync,

        is_ap_enabled:           _is_ap_enabled,
        is_ap_enabled_sync:      _is_ap_enabled_sync,

        enable_ap_mode:          _enable_ap_mode,
        enable_wifi_mode:        _enable_wifi_mode,
    };
}
<commit_msg>fixed callback error in enable_ap_mode<commit_after>var _       = require("underscore")._,
    async   = require("async"),
    fs      = require("fs"),
    exec    = require("child_process").exec,
    iwlist     = require("./iwlist"),
    config  = require("../config.json");
var scanResult;

// Better template format
_.templateSettings = {
    interpolate: /\{\{(.+?)\}\}/g,
    evaluate :   /\{\[([\s\S]+?)\]\}/g
};

// Helper function to write a given template to a file based on a given
// context
function write_template_to_file(template_path, file_name, context, callback) {
    async.waterfall([

        function read_template_file(next_step) {
            fs.readFile(template_path, {encoding: "utf8"}, next_step);
        },

        function update_file(file_txt, next_step) {
            var template = _.template(file_txt);
            fs.writeFile(file_name, template(context), next_step);
        }

    ], callback);
}

/*****************************************************************************\
    Return a set of functions which we can use to manage and check our wifi
    connection information
\*****************************************************************************/
module.exports = function() {
    // Detect which wifi driver we should use, the rtl871xdrv or the nl80211
    exec("iw list", function(error, stdout, stderr) {
        if (stderr.match(/^nl80211 not found/)) {
            config.wifi_driver_type = "rtl871xdrv";
        }
        // console.log("config.wifi_driver_type = " + config.wifi_driver_type);
    });

    // Hack: this just assumes that the outbound interface will be "wlan0"

    // Define some globals
    var ifconfig_fields = {
        "hw_addr":         /HWaddr\s([^\s]+)/,
        "inet_addr":       /inet addr:([^\s]+)/,
    },  iwconfig_fields = {
        "ap_addr":         /Access Point:\s([^\s]+)/,
        "ap_ssid":         /ESSID:\"([^\"]+)\"/,
        "unassociated":    /(unassociated)\s+Nick/,
    },  last_wifi_info = null;

    // TODO: rpi-config-ap hardcoded, should derive from a constant

    // Get generic info on an interface
    var _get_wifi_info = function(callback) {
        var output = {
            hw_addr:      "<unknown>",
            inet_addr:    "<unknown>",
            unassociated: "<unknown>",
        };

        // Inner function which runs a given command and sets a bunch
        // of fields
        function run_command_and_set_fields(cmd, fields, callback) {
            exec(cmd, function(error, stdout, stderr) {
                if (error) return callback(error);
                for (var key in fields) {
                    re = stdout.match(fields[key]);
                    if (re && re.length > 1) {
                        output[key] = re[1];
                    }
                }
                callback(null);
            });
        }

        // Run a bunch of commands and aggregate info
        async.series([
            function run_ifconfig(next_step) {
                run_command_and_set_fields("ifconfig wlan0", ifconfig_fields, next_step);
            },
            function run_iwconfig(next_step) {
                run_command_and_set_fields("iwconfig wlan0", iwconfig_fields, next_step);
            },
        ], function(error) {
            last_wifi_info = output;
            return callback(error, output);
        });
    },

    _reboot_wireless_network = function(wlan_iface, callback) {
        async.series([
            function down(next_step) {
                exec("sudo ifdown " + wlan_iface, function(error, stdout, stderr) {
                    if (!error) console.log("ifdown " + wlan_iface + " successful...");
                    next_step();
                });
            },
            function up(next_step) {
                exec("sudo ifup " + wlan_iface, function(error, stdout, stderr) {
                    if (!error) console.log("ifup " + wlan_iface + " successful...");
                    next_step();
                });
            },
        ], callback);
    },

    // Wifi related functions
    _is_wifi_enabled_sync = function(info) {
        // If we are not an AP, and we have a valid
        // inet_addr - wifi is enabled!
        if (null        == _is_ap_enabled_sync(info) &&
            "<unknown>" != info["inet_addr"]         &&
            "<unknown>" == info["unassociated"] ) {
            return info["inet_addr"];
        }
        return null;
    },

    _is_wifi_enabled = function(callback) {
        _get_wifi_info(function(error, info) {
            if (error) return callback(error, null);
            return callback(null, _is_wifi_enabled_sync(info));
        });
    },

    // Access Point related functions
    _is_ap_enabled_sync = function(info) {
        // If the hw_addr matches the ap_addr
        // and the ap_ssid matches "rpi-config-ap"
        // then we are in AP mode
        var is_ap  =
            info["hw_addr"].toLowerCase() == info["ap_addr"].toLowerCase() &&
            info["ap_ssid"] == config.access_point.ssid;
        return (is_ap) ? info["hw_addr"].toLowerCase() : null;
    },

    _is_ap_enabled = function(callback) {
        _get_wifi_info(function(error, info) {
            if (error) return callback(error, null);
            return callback(null, _is_ap_enabled_sync(info));
        });
    },

    // Enables the accesspoint w/ bcast_ssid. This assumes that both
    // isc-dhcp-server and hostapd are installed using:
    // $sudo npm run-script provision
    _enable_ap_mode = function(bcast_ssid, callback) {
        _is_ap_enabled(function(error, result_addr) {
            if (error) {
                console.log("ERROR: " + error);
                return callback(error);
            }

            if (result_addr && !config.access_point.force_reconfigure) {
                console.log("\nAccess point is enabled with ADDR: " + result_addr);
                return callback(null);
            } else if (config.access_point.force_reconfigure) {
                console.log("\nForce reconfigure enabled - reset AP");
            } else {
                console.log("\nAP is not enabled yet... enabling...");
            }

            var context = config.access_point;
            context["enable_ap"] = true;
            context["wifi_driver_type"] = config.wifi_driver_type;

            // Here we need to actually follow the steps to enable the ap
            async.series([

                // Enable the access point ip and netmask + static
                // DHCP for the wlan0 interface
                function update_interfaces(next_step) {
                    write_template_to_file(
                        "./assets/etc/network/interfaces.ap.template",
                        "/etc/network/interfaces",
                        context, next_step);
                },

                // Enable DHCP conf, set authoritative mode and subnet
                function update_dhcpd(next_step) {
                    var context = config.access_point;
                    // We must enable this to turn on the access point
                    write_template_to_file(
                        "./assets/etc/dhcp/dhcpd.conf.template",
                        "/etc/dhcp/dhcpd.conf",
                        context, next_step);
                },

                // Enable the interface in the dhcp server
                function update_dhcp_interface(next_step) {
                    write_template_to_file(
                        "./assets/etc/default/isc-dhcp-server.template",
                        "/etc/default/isc-dhcp-server",
                        context, next_step);
                },

                // Enable hostapd.conf file
                function update_hostapd_conf(next_step) {
                    write_template_to_file(
                        "./assets/etc/hostapd/hostapd.conf.template",
                        "/etc/hostapd/hostapd.conf",
                        context, next_step);
                },

                function update_hostapd_default(next_step) {
                    write_template_to_file(
                        "./assets/etc/default/hostapd.template",
                        "/etc/default/hostapd",
                        context, next_step);
                },
                function scan_networks(next_step){
		_reboot_wireless_network(context.wifi_interface,function(){
                  iwlist(function(error,result){
                    scanResult = result;
                    console.log(JSON.stringify(result));
			next_step();
                  });
		});
                },

                function reboot_network_interfaces(next_step) {
                    _reboot_wireless_network(context.wifi_interface, next_step);
                },

                function restart_dhcp_service(next_step) {
                    exec("service isc-dhcp-server restart", function(error, stdout, stderr) {
                        //console.log(stdout);
                        if (!error) console.log("... dhcp server restarted!");
                        next_step();
                    });
                },

                function restart_hostapd_service(next_step) {
                    exec("service hostapd restart", function(error, stdout, stderr) {
                        //console.log(stdout);
                        if (!error) console.log("... hostapd restarted!");
                        next_step();
                    });
                },

                // TODO: Do we need to issue a reboot here?

            ], callback(scanResult)
        });
    },

    // Disables AP mode and reverts to wifi connection
    _enable_wifi_mode = function(connection_info, callback) {

        _is_wifi_enabled(function(error, result_ip) {
            if (error) return callback(error);

            if (result_ip) {
                console.log("\nWifi connection is enabled with IP: " + result_ip);
                return callback(null);
            }

            async.series([

                // Update /etc/network/interface with correct info...
                function update_interfaces(next_step) {
                    write_template_to_file(
                        "./assets/etc/network/interfaces.wifi.template",
                        "/etc/network/interfaces",
                        connection_info, next_step);
                },

                // Stop the DHCP server...
                function restart_dhcp_service(next_step) {
                    exec("service isc-dhcp-server stop", function(error, stdout, stderr) {
                        //console.log(stdout);
                        if (!error) console.log("... dhcp server stopped!");
                        next_step();
                    });
                },

                function reboot_network_interfaces(next_step) {
                    _reboot_wireless_network(config.wifi_interface, next_step);
                },

            ], callback);
        });

    };

    return {
        get_wifi_info:           _get_wifi_info,
        reboot_wireless_network: _reboot_wireless_network,

        is_wifi_enabled:         _is_wifi_enabled,
        is_wifi_enabled_sync:    _is_wifi_enabled_sync,

        is_ap_enabled:           _is_ap_enabled,
        is_ap_enabled_sync:      _is_ap_enabled_sync,

        enable_ap_mode:          _enable_ap_mode,
        enable_wifi_mode:        _enable_wifi_mode,
    };
}
<|endoftext|>


<commit_before>var express = require('express');
var fs = require('fs');
var path = require('path');
var M = require('../models');

var router = express.Router();

router.get('/', function(req, res, next) {
  var programFiles = path.resolve('./data/programs.json');
  fs.readFile(programFiles , 'utf8', function (err, data) {
    if (err) throw err;
    res.render('index', {
      programs: JSON.parse(data)
    });
  });
});

router.get('/player', function(req, res, next) {
  res.render('player', { title: 'Express' });
});

router.get('/qr/:lot?', function(req, res, next) {
  M.QR.find({lot: req.params.lot}, function(err, qrs) {
    res.render('qrlot', {
      lot: req.params.lot,
      qrs: qrs
    });
  });
});

module.exports = router;
<commit_msg>隐藏管理入口<commit_after>var express = require('express');
var fs = require('fs');
var path = require('path');
var M = require('../models');

var router = express.Router();

router.get('/hi2017', function(req, res, next) {
  var programFiles = path.resolve('./data/programs.json');
  fs.readFile(programFiles , 'utf8', function (err, data) {
    if (err) throw err;
    res.render('index', {
      programs: JSON.parse(data)
    });
  });
});

router.get('/player', function(req, res, next) {
  res.render('player', { title: 'Express' });
});

router.get('/qr/:lot?', function(req, res, next) {
  M.QR.find({lot: req.params.lot}, function(err, qrs) {
    res.render('qrlot', {
      lot: req.params.lot,
      qrs: qrs
    });
  });
});

module.exports = router;
<|endoftext|>


<commit_before>import {Promise} from 'bluebird'
import {Registry} from './registry'
import {Set} from 'immutable'
import * as u from './useful'
import {read, set, remove} from './firebase_actions'
import log4js from 'log4js'
import {TODO_TRX_PATH, DONE_TRX_PATH} from './settings'

let logger = log4js.getLogger('transactor')

function configureLogging() {
  if (!log4js.configured) {
    log4js.configure({
      appenders: [{
        type: 'console',
        layout: {
          type: 'pattern',
          pattern: '[%[%5.5p%]] - %m'
        }
      }]
    })
    logger.setLevel('DEBUG')
  }
}

// when aborted transaction should be rescheduled; in milliseconds
const rescheduleDelay = 100

class AbortError {
  constructor(msg) {
    this.msg = msg
  }
}

class UserAbort {
  constructor(msg) {
    this.msg = msg
  }
}

export function transactor(firebase, handlers, todoTrxRef, closedTrxRef) {

  todoTrxRef = todoTrxRef || firebase.child(TODO_TRX_PATH)
  closedTrxRef = closedTrxRef || firebase.child(DONE_TRX_PATH)

  if (!log4js.configured) configureLogging()

  const runs = {} // run id mapped to trData
  const trCountLimit = 30
  const waiting = []
  const trSummary = {aborted: 0, tried: 0, processed: 0}
  const registry = new Registry()
  let nextTrId = 0
  let nextRunId = 0
  let trCount = 0

  function _abort(id) {
    registry.cleanup(id)
    delete runs[id]
  }

  function refFromPath(path) {
    return firebase.child(path.join('/'))
  }

  function pushWaiting(val) {
    if (val == null) {
      throw new Error('val should be trx, not null/undefined')
    }
    waiting.push(val)
    tryConsumeWaiting()
  }

  function scheduleLater(trData) {
    setTimeout(() => {pushWaiting(trData)}, rescheduleDelay)
  }

  // --abortAndReschedule
  function abortAndReschedule(id) {
    if (runs[id] == null) {
      throw new Error('shouldnt abort transaction that is already aborted')
    }
    logger.debug(`CLEANUP & ABORT : tr no ${id}`)
    trSummary['aborted'] += 1
    scheduleLater(runs[id])
    _abort(id)
  }

  // --processTr
  function processTr(id) {
    logger.debug(`starting process ${id}`)
    const {type, data} = runs[id]
    const handler = handlers[type]
    if (handler == null) {
      throw new Error(`handler for type ${type} does not exist. Full trData: ${runs[id]}`)
    }

    if (runs[id] == null) {
      throw new Error('given run does not exist!')
    }

    return Promise.resolve()
      .then(() => {
        logger.debug(`starting handler ${id}`)
        trSummary['tried'] += 1
        return handler({
          abort: userAbort,
          change: userChange,
          getId: userGetId,
          push: userPush,
          read: userRead,
          set: userSet,
          update: userUpdate,
        }, data)
      })
      .catch((err) => {
        if (err instanceof UserAbort) {
          logger.debug(`user abort ${id}, msg: ${err.msg}`)
          return {error: err.msg}
        }
        throw err
      })
      .then((result) => {
        // even if no Error is thrown, the transaction might be aborted in the
        // very last moment. Better check for it (userabort is not relevant now, as
        // we want to process such transaction)
        if (runs[id] != null) {
          logger.debug(`FINISH: tr no ${id}`)
          let userAborted = runs[id].status === 'useraborted'
          let writes = userAborted ? [] : registry.writesByTrx.get(id)
          let writesRef = firebase.child('__internal/writes').child(id)
          trSummary['processed'] += 1
          runs[id].status = 'finishing'
          set(writesRef, writes)
          writes.forEach((write) => {
            // TODO immutable destructuring
            set(refFromPath(write.get('path')), write.get('value'))
          })
          let trData = runs[id]
          remove(writesRef)
          try {
            // TODO handle this better (some parts of result might get lost
            // here, so warn user about it)
            result = JSON.parse(JSON.stringify({result}))
          } catch (e) {
            console.log(`Error for transaction id=${id}, result cannot be saved\n to firebase (result=${result})`)
            result = {}
          }
          set(closedTrxRef.child(trData.frbId), {data: trData, ...result})
          remove(todoTrxRef.child(trData.frbId))
          registry.cleanup(id)
          delete runs[id]
        }
      })
      .catch((err) => {
        if (err instanceof AbortError) {
          logger.debug(`abort error ${id}`)
          return
        }
        throw err
      })

    function checkAbort() {
      //logger.debug(`Checking abort id: ${id}, inProcess: ${inProcess[id]}`)
      if (runs[id] == null || runs[id].status === 'useraborted') {
        logger.debug('checkAbort: throwing abort')
        throw new AbortError('Transaction was aborted')
      }
      if (runs[id] === 'useraborted') {
        logger.debug('checkAbort: throwing useraborted')
        throw new AbortError('Transaction was aborted')
      }

    }

    function handlePossibleConflict(_conflict) {
      if (_conflict.constructor !== Set) {
        throw new Error('conflict must be of a type immutable.Set')
      }
      checkAbort()
      let conflict = _conflict.delete(id)
      let conflictTrIds = conflict.map((i) => runs[i].trId)
      let trId = runs[id].trId
      let finishingTrIds = conflict.filter((i) => runs[i].status === 'finishing').map((i) => runs[i].trId)
      if (!conflict.isEmpty()) {
        if (u.any(conflict, (i) => runs[i].status === 'finishing') ||
            conflictTrIds.min() < trId) {
          logger.debug(`aborting ${trId}, because of ${conflictTrIds}, finishing: ${finishingTrIds}`)
          abortAndReschedule(id)
        } else {
          logger.debug(`aborting ${conflictTrIds}, because of ${trId}, finishing: ${finishingTrIds}`)
          conflict.forEach(abortAndReschedule)
        }
      }
      checkAbort()
    }

    // TODO if possible, make DB operations accept also firebase ref
    function userRead(path) {
      if (u.any(path, (e) => (e == null))) throw new Error(`READ ERROR: undefined / null present in path: ${path}`)
      handlePossibleConflict(registry.conflictingWithRead(path))
      registry.addRead(id, path)
      return Promise.resolve()
        .then(() => read(refFromPath(path)))
        .then((val) => {
          checkAbort()
          return registry.readAsIfTrx(id, path, val)
        })
    }

    function userSet(path, value) {
      handlePossibleConflict(registry.conflictingWithWrite(path))
      registry.addWrite(id, path, value)
    }

    function userAbort(msg) {
      runs[id].abortMsg = msg
      runs[id].status = 'useraborted'
      throw new UserAbort(msg)
    }

    function userPush(path, value) {
      let ref = refFromPath(path).push()
      userSet([...path, ref.key()], value)
      return ref
    }

    function userGetId() {
      return firebase.push().key()
    }

    function userChange(path, updateFn) {
      return userRead(path)
        .then((snapshot) => userSet(path, updateFn(snapshot)))
    }

    function userUpdate(path, values) {
      if ((values == null) || (values.constructor !== Object)) {
        throw new Error(`The value argument in update must be a JS Object, found ${values}`)
      }
      Object.keys(values).forEach((key) => userSet([...path, key], values[key]))
    }

  }

  function tryConsumeWaiting() {
    while (waiting.length > 0 && trCount < trCountLimit) {
      trCount += 1
      let trData = waiting.shift()
      let id = nextRunId++
      runs[id] = {...trData, status: 'inprocess', id}
      processTr(id)
        .then(() => {
          trCount -= 1
          tryConsumeWaiting()
        })
    }
  }

  let onChildAdded = todoTrxRef.on('child_added', (childSnapshot, prevChildKey) => {
    let trData = childSnapshot.val()
    if (trData.type == null) {
      logger.error('malformed data: ', trData)
      throw new Error('malformed trData')
    }
    trData.trId = nextTrId++
    logger.debug(`SCHEDULED: tr no ${trData.trId} data: ${JSON.stringify(trData)}`)
    trData.frbId = childSnapshot.key()
    pushWaiting(trData)
  })

  return {
    'stop': () => todoTrxRef.off('child_added', onChildAdded),
    'registry': registry,
    'trSummary': trSummary,
  }

}

// Synchronous variant of finishing transaction code
//let toWait = []
//toWait.push(set(writesRef, writes))
//writes.forEach((write) => {
//  // TODO immutable destructuring
//  toWait.push(set(refFromPath(write.get('path')), write.get('value')))
//})
//let trData = runs[id]
//  Promise.all(toWait).then((_) => {
//  remove(writesRef)
//  set(firebase.child(DONE_TRX_PATH).child(trData.frbId), trData)
//  remove(firebase.child(TODO_TRX_PATH).child(trData.frbId))
//  registry.cleanup(id)
//  delete runs[id]
//})
<commit_msg>bug: transactor fails, when no writes are done<commit_after>import {Promise} from 'bluebird'
import {Registry} from './registry'
import {Set} from 'immutable'
import * as u from './useful'
import {read, set, remove} from './firebase_actions'
import log4js from 'log4js'
import {TODO_TRX_PATH, DONE_TRX_PATH} from './settings'

let logger = log4js.getLogger('transactor')

function configureLogging() {
  if (!log4js.configured) {
    log4js.configure({
      appenders: [{
        type: 'console',
        layout: {
          type: 'pattern',
          pattern: '[%[%5.5p%]] - %m'
        }
      }]
    })
    logger.setLevel('DEBUG')
  }
}

// when aborted transaction should be rescheduled; in milliseconds
const rescheduleDelay = 100

class AbortError {
  constructor(msg) {
    this.msg = msg
  }
}

class UserAbort {
  constructor(msg) {
    this.msg = msg
  }
}

export function transactor(firebase, handlers, todoTrxRef, closedTrxRef) {

  todoTrxRef = todoTrxRef || firebase.child(TODO_TRX_PATH)
  closedTrxRef = closedTrxRef || firebase.child(DONE_TRX_PATH)

  if (!log4js.configured) configureLogging()

  const runs = {} // run id mapped to trData
  const trCountLimit = 30
  const waiting = []
  const trSummary = {aborted: 0, tried: 0, processed: 0}
  const registry = new Registry()
  let nextTrId = 0
  let nextRunId = 0
  let trCount = 0

  function _abort(id) {
    registry.cleanup(id)
    delete runs[id]
  }

  function refFromPath(path) {
    return firebase.child(path.join('/'))
  }

  function pushWaiting(val) {
    if (val == null) {
      throw new Error('val should be trx, not null/undefined')
    }
    waiting.push(val)
    tryConsumeWaiting()
  }

  function scheduleLater(trData) {
    setTimeout(() => {pushWaiting(trData)}, rescheduleDelay)
  }

  // --abortAndReschedule
  function abortAndReschedule(id) {
    if (runs[id] == null) {
      throw new Error('shouldnt abort transaction that is already aborted')
    }
    logger.debug(`CLEANUP & ABORT : tr no ${id}`)
    trSummary['aborted'] += 1
    scheduleLater(runs[id])
    _abort(id)
  }

  // --processTr
  function processTr(id) {
    logger.debug(`starting process ${id}`)
    const {type, data} = runs[id]
    const handler = handlers[type]
    if (handler == null) {
      throw new Error(`handler for type ${type} does not exist. Full trData: ${runs[id]}`)
    }

    if (runs[id] == null) {
      throw new Error('given run does not exist!')
    }

    return Promise.resolve()
      .then(() => {
        logger.debug(`starting handler ${id}`)
        trSummary['tried'] += 1
        return handler({
          abort: userAbort,
          change: userChange,
          getId: userGetId,
          push: userPush,
          read: userRead,
          set: userSet,
          update: userUpdate,
        }, data)
      })
      .catch((err) => {
        if (err instanceof UserAbort) {
          logger.debug(`user abort ${id}, msg: ${err.msg}`)
          return {error: err.msg}
        }
        throw err
      })
      .then((result) => {
        // even if no Error is thrown, the transaction might be aborted in the
        // very last moment. Better check for it (userabort is not relevant now, as
        // we want to process such transaction)
        if (runs[id] != null) {
          logger.debug(`FINISH: tr no ${id}`)
          let userAborted = runs[id].status === 'useraborted'
          let writes = userAborted ? [] : registry.writesByTrx.get(id, [])
          let writesRef = firebase.child('__internal/writes').child(id)
          trSummary['processed'] += 1
          runs[id].status = 'finishing'
          set(writesRef, writes)
          writes.forEach((write) => {
            // TODO immutable destructuring
            set(refFromPath(write.get('path')), write.get('value'))
          })
          let trData = runs[id]
          remove(writesRef)
          try {
            // TODO handle this better (some parts of result might get lost
            // here, so warn user about it)
            result = JSON.parse(JSON.stringify({result}))
          } catch (e) {
            console.log(`Error for transaction id=${id}, result cannot be saved\n to firebase (result=${result})`)
            result = {}
          }
          set(closedTrxRef.child(trData.frbId), {data: trData, ...result})
          remove(todoTrxRef.child(trData.frbId))
          registry.cleanup(id)
          delete runs[id]
        }
      })
      .catch((err) => {
        if (err instanceof AbortError) {
          logger.debug(`abort error ${id}`)
          return
        }
        throw err
      })

    function checkAbort() {
      //logger.debug(`Checking abort id: ${id}, inProcess: ${inProcess[id]}`)
      if (runs[id] == null || runs[id].status === 'useraborted') {
        logger.debug('checkAbort: throwing abort')
        throw new AbortError('Transaction was aborted')
      }
      if (runs[id] === 'useraborted') {
        logger.debug('checkAbort: throwing useraborted')
        throw new AbortError('Transaction was aborted')
      }

    }

    function handlePossibleConflict(_conflict) {
      if (_conflict.constructor !== Set) {
        throw new Error('conflict must be of a type immutable.Set')
      }
      checkAbort()
      let conflict = _conflict.delete(id)
      let conflictTrIds = conflict.map((i) => runs[i].trId)
      let trId = runs[id].trId
      let finishingTrIds = conflict.filter((i) => runs[i].status === 'finishing').map((i) => runs[i].trId)
      if (!conflict.isEmpty()) {
        if (u.any(conflict, (i) => runs[i].status === 'finishing') ||
            conflictTrIds.min() < trId) {
          logger.debug(`aborting ${trId}, because of ${conflictTrIds}, finishing: ${finishingTrIds}`)
          abortAndReschedule(id)
        } else {
          logger.debug(`aborting ${conflictTrIds}, because of ${trId}, finishing: ${finishingTrIds}`)
          conflict.forEach(abortAndReschedule)
        }
      }
      checkAbort()
    }

    // TODO if possible, make DB operations accept also firebase ref
    function userRead(path) {
      if (u.any(path, (e) => (e == null))) throw new Error(`READ ERROR: undefined / null present in path: ${path}`)
      handlePossibleConflict(registry.conflictingWithRead(path))
      registry.addRead(id, path)
      return Promise.resolve()
        .then(() => read(refFromPath(path)))
        .then((val) => {
          checkAbort()
          return registry.readAsIfTrx(id, path, val)
        })
    }

    function userSet(path, value) {
      handlePossibleConflict(registry.conflictingWithWrite(path))
      registry.addWrite(id, path, value)
    }

    function userAbort(msg) {
      runs[id].abortMsg = msg
      runs[id].status = 'useraborted'
      throw new UserAbort(msg)
    }

    function userPush(path, value) {
      let ref = refFromPath(path).push()
      userSet([...path, ref.key()], value)
      return ref
    }

    function userGetId() {
      return firebase.push().key()
    }

    function userChange(path, updateFn) {
      return userRead(path)
        .then((snapshot) => userSet(path, updateFn(snapshot)))
    }

    function userUpdate(path, values) {
      if ((values == null) || (values.constructor !== Object)) {
        throw new Error(`The value argument in update must be a JS Object, found ${values}`)
      }
      Object.keys(values).forEach((key) => userSet([...path, key], values[key]))
    }

  }

  function tryConsumeWaiting() {
    while (waiting.length > 0 && trCount < trCountLimit) {
      trCount += 1
      let trData = waiting.shift()
      let id = nextRunId++
      runs[id] = {...trData, status: 'inprocess', id}
      processTr(id)
        .then(() => {
          trCount -= 1
          tryConsumeWaiting()
        })
    }
  }

  let onChildAdded = todoTrxRef.on('child_added', (childSnapshot, prevChildKey) => {
    let trData = childSnapshot.val()
    if (trData.type == null) {
      logger.error('malformed data: ', trData)
      throw new Error('malformed trData')
    }
    trData.trId = nextTrId++
    logger.debug(`SCHEDULED: tr no ${trData.trId} data: ${JSON.stringify(trData)}`)
    trData.frbId = childSnapshot.key()
    pushWaiting(trData)
  })

  return {
    'stop': () => todoTrxRef.off('child_added', onChildAdded),
    'registry': registry,
    'trSummary': trSummary,
  }

}

// Synchronous variant of finishing transaction code
//let toWait = []
//toWait.push(set(writesRef, writes))
//writes.forEach((write) => {
//  // TODO immutable destructuring
//  toWait.push(set(refFromPath(write.get('path')), write.get('value')))
//})
//let trData = runs[id]
//  Promise.all(toWait).then((_) => {
//  remove(writesRef)
//  set(firebase.child(DONE_TRX_PATH).child(trData.frbId), trData)
//  remove(firebase.child(TODO_TRX_PATH).child(trData.frbId))
//  registry.cleanup(id)
//  delete runs[id]
//})
<|endoftext|>


<commit_before>function SwaggerService(baseUrl, _apiKey, statusCallback) {
  if (!baseUrl)
  throw new Error("baseUrl must be passed while creating SwaggerService");

  // constants
  baseUrl = jQuery.trim(baseUrl);
  if (baseUrl.length == 0)
  throw new Error("baseUrl must be passed while creating SwaggerService");

  if (! (baseUrl.toLowerCase().indexOf("http:") == 0 || baseUrl.toLowerCase().indexOf("https:") == 0)) {
    baseUrl = ("http://" + baseUrl);
  }

  var apiHost = baseUrl.substr(0, baseUrl.lastIndexOf("/"));
  var discoParts = baseUrl.split("/");
  var rootResourcesApiName = discoParts[discoParts.length-1];
  var formatString = ".{format}";
  var statusListener = statusCallback;
  var apiKey = _apiKey;

  var apiKeySuffix = "";
  if (apiKey) {
    apiKey = jQuery.trim(apiKey);
    if (apiKey.length > 0)
    apiKeySuffix = "?api_key=" + apiKey;
  }

  function log() {
    if (window.console) console.log.apply(console,arguments);
  }

  function error(m) {
    log("ERROR: " + m);
  }

  function updateStatus(status) {
    statusListener(status);
  }

  // make some models public
  this.ApiResource = function() {
    return ApiResource;
  };

  this.apiHost = function() {
    return apiHost;
  };

  this.formatString = function() {
    return formatString;
  };

  // Model: ApiResource
  var ApiResource = Spine.Model.setup("ApiResource", ["name", "baseUrl", "path", "path_json", "path_xml", "description", "apiLists", "modelList"]);
  ApiResource.include({
    path_json: null,
    path_xml: null,

    init: function(atts) {
      if (atts) this.load(atts);
      this.path_json = this.path.replace("{format}", "json");
      this.path_xml = this.path.replace("{format}", "xml");
      this.baseUrl = apiHost;
      //execluded 9 letters to remove .{format} from name
      this.name = this.path.split("/");
      this.name = this.name[this.name.length - 1];
      this.name = this.name.replace(".{format}",'').replace(/\//g, "_");
      // this.name = this.path.substr(1, this.path.length - formatString.length - 1).replace(/\//g, "_");
      this.apiList = Api.sub();
      this.modelList = ApiModel.sub();
    },

    addApis: function(apiObjects) {
      // log("apiObjects: %o", apiObjects);
      this.apiList.createAll(apiObjects);
    },

    addModel: function(modelObject) {
      this.modelList.create(modelObject);
    },

    toString: function() {
      return this.path_json + ": " + this.description;
    }
  });

  // Model: Api
  var Api = Spine.Model.setup("Api", ["baseUrl", "path", "path_json", "path_xml", "name", "description", "operations", "path_json", "path_xml"]);
  Api.include({
    init: function(atts) {
      if (atts) this.load(atts);

      this.baseUrl = apiHost;

      var secondPathSeperatorIndex = this.path.indexOf("/", 1);
      if (secondPathSeperatorIndex > 0) {
        var prefix = this.path.substr(0, secondPathSeperatorIndex);
        var suffix = this.path.substr(secondPathSeperatorIndex, this.path.length);
        // log(this.path + ":: " + prefix + "..." + suffix);
        this.path_json = prefix.replace("{format}", "json") + suffix;
        this.path_xml = prefix.replace("{format}", "xml") + suffix;;

        if (this.path.indexOf("/") == 0) {
          this.name = this.path.substr(1, secondPathSeperatorIndex - formatString.length - 1);
        } else {
          this.name = this.path.substr(0, secondPathSeperatorIndex - formatString.length - 1);
        }
      } else {
        this.path_json = this.path.replace("{format}", "json");
        this.path_xml = this.path.replace("{format}", "xml");

        if (this.path.indexOf("/") == 0) {
          this.name = this.path.substr(1, this.path.length - formatString.length - 1);
        } else {
          this.name = this.path.substr(0, this.path.length - formatString.length - 1);
        }
      }

      var value = this.operations;

      this.operations = ApiOperation.sub();
      if (value) {
        for (var i = 0; i < value.length; i++) {
          var obj = value[i];
          obj.apiName = this.name;
          obj.path = this.path;
          obj.path_json = this.path_json;
          obj.path_xml = this.path_xml;

        }

        this.operations.refresh(value);
      }

      updateStatus("Loading " + this.path + "...");

    },

    toString: function() {
      var opsString = "";
      for (var i = 0; i < this.operations.all().length; i++) {
        var e = this.operations.all()[i];

        if (opsString.length > 0)
        opsString += ", ";

        opsString += e.toString();
      }
      return this.path_json + "- " + this.operations.all().length + " operations: " + opsString;
    }

  });

  // Model: ApiOperation
  var ApiOperation = Spine.Model.setup("ApiOperation", ["baseUrl", "path", "path_json", "path_xml", "summary", "notes", "deprecated", "open", "httpMethod", "httpMethodLowercase", "nickname", "responseClass", "parameters", "apiName"]);
  ApiOperation.include({
    init: function(atts) {
      if (atts) this.load(atts);

      this.baseUrl = apiHost;
      this.httpMethodLowercase = this.httpMethod.toLowerCase();

      var value = this.parameters;
      this.parameters = ApiParameter.sub();
      if (value) this.parameters.refresh(value);
    },

    toString: function() {
      var paramsString = "(";
      for (var i = 0; i < this.parameters.all().length; i++) {
        var e = this.parameters.all()[i];

        if (paramsString.length > 1)
        paramsString += ", ";

        paramsString += e.toString();
      }
      paramsString += ")";

      return "{" + this.path_json + "| " + this.nickname + paramsString + ": " + this.summary + "}";
    },

    invocationUrl: function(formValues) {
      var formValuesMap = new Object();
      for (var i = 0; i < formValues.length; i++) {
        var formValue = formValues[i];
        if (formValue.value && jQuery.trim(formValue.value).length > 0)
        formValuesMap[formValue.name] = formValue.value;
      }

      var urlTemplateText = this.path_json.split("{").join("${");
      // log("url template = " + urlTemplateText);
      var urlTemplate = $.template(null, urlTemplateText);
      var url = $.tmpl(urlTemplate, formValuesMap)[0].data;
      // log("url with path params = " + url);

      var queryParams = apiKeySuffix;
      this.parameters.each(function(param) {
        var paramValue = jQuery.trim(formValuesMap[param.name]);
        if (param.paramType == "query" && paramValue.length > 0) {
          queryParams += queryParams.length > 0 ? "&": "?";
          queryParams += param.name;
          queryParams += "=";
          queryParams += formValuesMap[param.name];
        }
      });

      url = this.baseUrl + url + queryParams;
      // log("final url with query params and base url = " + url);

      return url;
    }

  });

  // Model: ApiParameter
  var ApiParameter = Spine.Model.setup("ApiParameter", ["name", "description", "required", "dataType", "allowableValues", "paramType", "allowMultiple"]);
  ApiParameter.include({
    init: function(atts) {
      if (atts) this.load(atts);

      this.name = this.name || this.dataType;

      if(this.allowableValues){
          var value = this.allowableValues;
          if(value.valueType == "LIST"){
              this.allowableValues = AllowableListValues.sub();
          } else if (value.valueType == "RANGE"){
              this.allowableValues = AllowableRangeValues.sub();
          }
          if (value) this.allowableValues = this.allowableValues.create(value);
      }
    },

    toString: function() {
      if (this.allowableValues)
      return this.name + ": " + this.dataType + " " + this.allowableValues;
      else
      return this.name + ": " + this.dataType;
    }

  });

  var AllowableListValues = Spine.Model.setup("AllowableListValues", ["valueType", "values"]);
  AllowableListValues.include({
    init: function(atts) {
      if (atts) this.load(atts);
      this.name = "allowableValues";
    },
    toString: function() {
      if (this.values)
      return  "["+this.values+"]";
      else
      return "";
    }
  });

  var AllowableRangeValues = Spine.Model.setup("AllowableRangeValues", ["valueType", "inclusive", "min", "max"]);
  AllowableRangeValues.include({
    init: function(atts) {
      if (atts) this.load(atts);
      this.name = "allowableValues";
    },

    toString: function() {
      if (this.min && this.max)
      return "[" + min + "," + max + "]";
      else
      return "";
    }
  });

  // Model: ApiModel
  var ApiModel = Spine.Model.setup("ApiModel", ["id", "fields"]);
  ApiModel.include({
    init: function(atts) {
      if (atts) this.load(atts);

      if (!this.fields) {
        var propertiesListObject = this.properties;
        this.fields = ApiModelProperty.sub();

        for (var propName in propertiesListObject) {
          if (propName != "parent") {
            var p = propertiesListObject[propName];
            p.name = propName;
            p.id = Spine.guid();
            // log(p);
            this.fields.create(p);
          }
        }
        //log("got " + this.fields.count() + " fields for " + this.id);
      }
    },

    toString: function() {
      var propsString = "";

      propsString += "(";
      for (var i = 0; i < this.fields.all().length; i++) {
        var e = this.fields.all()[i];

        if (propsString.length > 1)
        propsString += ", ";

        propsString += e.toString();
      }
      propsString += ")";

      if (this.required)
      return this.id + " (required): " + propsString;
      else
      return this.id + ": " + propsString;
    }

  });

  // Model: ApiModelProperty
  var ApiModelProperty = Spine.Model.setup("ApiModelProperty", ["name", "required", "dataType"]);
  ApiModelProperty.include({
    init: function(atts) {
      if (atts) this.load(atts);

      if (!this.dataType) {
        if (atts.type == "any")
        this.dataType = "object";
        else if (atts.type == "array") {
          if (atts.items) {
            if (atts.items.$ref) {
              this.dataType = "array[" + atts.items.$ref + "]";
            } else {
              this.dataType = "array[" + atts.items.type + "]";
            }
          } else {
            this.dataType = "array";
          }
        } else
        this.dataType = atts.type;

      }
    },

    toString: function() {
      if (this.required)
      return this.name + ": " + this.dataType + " (required)";
      else
      return this.name + ": " + this.dataType;
    }

  });

  // Controller
  var ModelController = Spine.Controller.create({
    countLoaded: 0,
    proxied: ["fetchResources", "loadResources", "apisLoaded", "modelsLoaded"],

    init: function() {
      // log("ModelController.init");

      this.fetchEndpoints();
    },

    fetchEndpoints: function() {
      var controller = this;

      updateStatus("Fetching API List...");
      var url = apiHost + "/" + rootResourcesApiName + apiKeySuffix;
      $.getJSON(url, function(response) {

        // if (response.apis) {
          ApiResource.createAll(response.apis);
        // }

        // get rid of the root resource list api since we're not going to document that
        var obj = ApiResource.findByAttribute("name", rootResourcesApiName);
        if (obj)
          obj.destroy();
        controller.fetchResources();
      });
    },

    fetchResources: function() {
      //log("fetchResources");
      //ApiResource.logAll();
      for (var i = 0; i < ApiResource.all().length; i++) {
        var apiResource = ApiResource.all()[i];
        this.fetchResource(apiResource);
      }
    },

    fetchResource: function(apiResource) {
      var controller = this;
      updateStatus("Fetching " + apiResource.name + "...");
      var resourceUrl = apiHost + apiResource.path_json + apiKeySuffix;
      // log("resourceUrl: %o", resourceUrl);
      $.getJSON(resourceUrl,
      function(response) {
        controller.loadResources(response, apiResource);
      });
    },

    loadResources: function(response, apiResource) {
      try {
        this.countLoaded++;
        //    log(response);
        // if (response.apis) {
          apiResource.addApis(response.apis);
        // }
        //        updateStatus("Parsed Apis");
        //log(response.models);
        if (response.models) {
          // log("response.models.length = " + response.models.length);
          for (var modeName in response.models) {
            var m = response.models[modeName];
            // log("creating " + m.id);
            apiResource.addModel(m);
            //      apiResource.modelList.create(m);
          }
        }

        updateStatus();
      } finally {
        if (this.countLoaded == ApiResource.count()) {
          // log("all models/api loaded");
          ApiResource.trigger("refresh");
        }
      }
    }

  });

  this.init = function() {
    this.modelController = ModelController.init();
  };
};

<commit_msg>using different basePath variants to get API Listing<commit_after>function SwaggerService(discoveryUrl, _apiKey, statusCallback) {
  if (!discoveryUrl)
  throw new Error("discoveryUrl must be passed while creating SwaggerService");

  // constants
  discoveryUrl = jQuery.trim(discoveryUrl);
  if (discoveryUrl.length == 0)
  throw new Error("discoveryUrl must be passed while creating SwaggerService");

  if (! (discoveryUrl.toLowerCase().indexOf("http:") == 0 || discoveryUrl.toLowerCase().indexOf("https:") == 0)) {
    discoveryUrl = ("http://" + discoveryUrl);
  }

  var globalBasePath = null;
  var formatString = ".{format}";
  var statusListener = statusCallback;
  var apiKey = _apiKey;

  var apiKeySuffix = "";
  if (apiKey) {
    apiKey = jQuery.trim(apiKey);
    if (apiKey.length > 0)
    apiKeySuffix = "?api_key=" + apiKey;
  }

  function log() {
    if (window.console) console.log.apply(console,arguments);
  }

  function error(m) {
    log("ERROR: " + m);
  }

  function updateStatus(status) {
    statusListener(status);
  }

  function endsWith(str, suffix) {
    return str.indexOf(suffix, str.length - suffix.length) !== -1;
  }

  // make some models public
  this.ApiResource = function() {
    return ApiResource;
  };

  this.apiHost = function() {
    return globalBasePath;
  };

  this.formatString = function() {
    return formatString;
  };

  // Model: ApiResource
  var ApiResource = Spine.Model.setup("ApiResource", ["name", "baseUrl", "path", "path_json", "path_xml", "description", "apiLists", "modelList"]);
  ApiResource.include({
    path_json: null,
    path_xml: null,

    init: function(atts) {
      if (atts) this.load(atts);
      this.path_json = this.path.replace("{format}", "json");
      this.path_xml = this.path.replace("{format}", "xml");
      this.baseUrl = globalBasePath;
      //execluded 9 letters to remove .{format} from name
      this.name = this.path.split("/");
      this.name = this.name[this.name.length - 1];
      this.name = this.name.replace(".{format}",'').replace(/\//g, "_");
      // this.name = this.path.substr(1, this.path.length - formatString.length - 1).replace(/\//g, "_");
      this.apiList = Api.sub();
      this.modelList = ApiModel.sub();
    },

    addApis: function(apiObjects) {
      // log("apiObjects: %o", apiObjects);
      this.apiList.createAll(apiObjects);
    },

    addModel: function(modelObject) {
      this.modelList.create(modelObject);
    },

    toString: function() {
      return this.path_json + ": " + this.description;
    }
  });

  // Model: Api
  var Api = Spine.Model.setup("Api", ["baseUrl", "path", "path_json", "path_xml", "name", "description", "operations", "path_json", "path_xml"]);
  Api.include({
    init: function(atts) {
      if (atts) this.load(atts);

      this.baseUrl = globalBasePath;

      var secondPathSeperatorIndex = this.path.indexOf("/", 1);
      if (secondPathSeperatorIndex > 0) {
        var prefix = this.path.substr(0, secondPathSeperatorIndex);
        var suffix = this.path.substr(secondPathSeperatorIndex, this.path.length);
        // log(this.path + ":: " + prefix + "..." + suffix);
        this.path_json = prefix.replace("{format}", "json") + suffix;
        this.path_xml = prefix.replace("{format}", "xml") + suffix;;

        if (this.path.indexOf("/") == 0) {
          this.name = this.path.substr(1, secondPathSeperatorIndex - formatString.length - 1);
        } else {
          this.name = this.path.substr(0, secondPathSeperatorIndex - formatString.length - 1);
        }
      } else {
        this.path_json = this.path.replace("{format}", "json");
        this.path_xml = this.path.replace("{format}", "xml");

        if (this.path.indexOf("/") == 0) {
          this.name = this.path.substr(1, this.path.length - formatString.length - 1);
        } else {
          this.name = this.path.substr(0, this.path.length - formatString.length - 1);
        }
      }

      var value = this.operations;

      this.operations = ApiOperation.sub();
      if (value) {
        for (var i = 0; i < value.length; i++) {
          var obj = value[i];
          obj.apiName = this.name;
          obj.path = this.path;
          obj.path_json = this.path_json;
          obj.path_xml = this.path_xml;

        }

        this.operations.refresh(value);
      }

      updateStatus("Loading " + this.path + "...");

    },

    toString: function() {
      var opsString = "";
      for (var i = 0; i < this.operations.all().length; i++) {
        var e = this.operations.all()[i];

        if (opsString.length > 0)
        opsString += ", ";

        opsString += e.toString();
      }
      return this.path_json + "- " + this.operations.all().length + " operations: " + opsString;
    }

  });

  // Model: ApiOperation
  var ApiOperation = Spine.Model.setup("ApiOperation", ["baseUrl", "path", "path_json", "path_xml", "summary", "notes", "deprecated", "open", "httpMethod", "httpMethodLowercase", "nickname", "responseClass", "parameters", "apiName"]);
  ApiOperation.include({
    init: function(atts) {
      if (atts) this.load(atts);

      this.baseUrl = globalBasePath;
      this.httpMethodLowercase = this.httpMethod.toLowerCase();

      var value = this.parameters;
      this.parameters = ApiParameter.sub();
      if (value) this.parameters.refresh(value);
    },

    toString: function() {
      var paramsString = "(";
      for (var i = 0; i < this.parameters.all().length; i++) {
        var e = this.parameters.all()[i];

        if (paramsString.length > 1)
        paramsString += ", ";

        paramsString += e.toString();
      }
      paramsString += ")";

      return "{" + this.path_json + "| " + this.nickname + paramsString + ": " + this.summary + "}";
    },

    invocationUrl: function(formValues) {
      var formValuesMap = new Object();
      for (var i = 0; i < formValues.length; i++) {
        var formValue = formValues[i];
        if (formValue.value && jQuery.trim(formValue.value).length > 0)
        formValuesMap[formValue.name] = formValue.value;
      }

      var urlTemplateText = this.path_json.split("{").join("${");
      // log("url template = " + urlTemplateText);
      var urlTemplate = $.template(null, urlTemplateText);
      var url = $.tmpl(urlTemplate, formValuesMap)[0].data;
      // log("url with path params = " + url);

      var queryParams = apiKeySuffix;
      this.parameters.each(function(param) {
        var paramValue = jQuery.trim(formValuesMap[param.name]);
        if (param.paramType == "query" && paramValue.length > 0) {
          queryParams += queryParams.length > 0 ? "&": "?";
          queryParams += param.name;
          queryParams += "=";
          queryParams += formValuesMap[param.name];
        }
      });

      url = this.baseUrl + url + queryParams;
      // log("final url with query params and base url = " + url);

      return url;
    }

  });

  // Model: ApiParameter
  var ApiParameter = Spine.Model.setup("ApiParameter", ["name", "description", "required", "dataType", "allowableValues", "paramType", "allowMultiple"]);
  ApiParameter.include({
    init: function(atts) {
      if (atts) this.load(atts);

      this.name = this.name || this.dataType;

      if(this.allowableValues){
          var value = this.allowableValues;
          if(value.valueType == "LIST"){
              this.allowableValues = AllowableListValues.sub();
          } else if (value.valueType == "RANGE"){
              this.allowableValues = AllowableRangeValues.sub();
          }
          if (value) this.allowableValues = this.allowableValues.create(value);
      }
    },

    toString: function() {
      if (this.allowableValues)
      return this.name + ": " + this.dataType + " " + this.allowableValues;
      else
      return this.name + ": " + this.dataType;
    }

  });

  var AllowableListValues = Spine.Model.setup("AllowableListValues", ["valueType", "values"]);
  AllowableListValues.include({
    init: function(atts) {
      if (atts) this.load(atts);
      this.name = "allowableValues";
    },
    toString: function() {
      if (this.values)
      return  "["+this.values+"]";
      else
      return "";
    }
  });

  var AllowableRangeValues = Spine.Model.setup("AllowableRangeValues", ["valueType", "inclusive", "min", "max"]);
  AllowableRangeValues.include({
    init: function(atts) {
      if (atts) this.load(atts);
      this.name = "allowableValues";
    },

    toString: function() {
      if (this.min && this.max)
      return "[" + min + "," + max + "]";
      else
      return "";
    }
  });

  // Model: ApiModel
  var ApiModel = Spine.Model.setup("ApiModel", ["id", "fields"]);
  ApiModel.include({
    init: function(atts) {
      if (atts) this.load(atts);

      if (!this.fields) {
        var propertiesListObject = this.properties;
        this.fields = ApiModelProperty.sub();

        for (var propName in propertiesListObject) {
          if (propName != "parent") {
            var p = propertiesListObject[propName];
            p.name = propName;
            p.id = Spine.guid();
            // log(p);
            this.fields.create(p);
          }
        }
        //log("got " + this.fields.count() + " fields for " + this.id);
      }
    },

    toString: function() {
      var propsString = "";

      propsString += "(";
      for (var i = 0; i < this.fields.all().length; i++) {
        var e = this.fields.all()[i];

        if (propsString.length > 1)
        propsString += ", ";

        propsString += e.toString();
      }
      propsString += ")";

      if (this.required)
      return this.id + " (required): " + propsString;
      else
      return this.id + ": " + propsString;
    }

  });

  // Model: ApiModelProperty
  var ApiModelProperty = Spine.Model.setup("ApiModelProperty", ["name", "required", "dataType"]);
  ApiModelProperty.include({
    init: function(atts) {
      if (atts) this.load(atts);

      if (!this.dataType) {
        if (atts.type == "any")
        this.dataType = "object";
        else if (atts.type == "array") {
          if (atts.items) {
            if (atts.items.$ref) {
              this.dataType = "array[" + atts.items.$ref + "]";
            } else {
              this.dataType = "array[" + atts.items.type + "]";
            }
          } else {
            this.dataType = "array";
          }
        } else
        this.dataType = atts.type;

      }
    },

    toString: function() {
      if (this.required)
      return this.name + ": " + this.dataType + " (required)";
      else
      return this.name + ": " + this.dataType;
    }

  });

  // Controller
  var ModelController = Spine.Controller.create({
    countLoaded: 0,
    proxied: ["fetchResources", "loadResources", "apisLoaded", "modelsLoaded"],
    discoveryUrlList: [],
	discoveryUrlListCursor: 0,

    init: function() {
      // log("ModelController.init");

      this.fetchEndpoints();
    },

    fetchEndpoints: function() {
      updateStatus("Fetching API List...");
	  var baseDiscoveryUrl = endsWith(discoveryUrl, "/") ? discoveryUrl.substr(0, discoveryUrl.length - 1) : discoveryUrl;
	  if(endsWith(baseDiscoveryUrl, "/resources.json"))
		baseDiscoveryUrl = baseDiscoveryUrl.substr(0, baseDiscoveryUrl.length - "/resources.json".length);
	  else if(endsWith(baseDiscoveryUrl, "/resources"))
		baseDiscoveryUrl = baseDiscoveryUrl.substr(0, baseDiscoveryUrl.length - "/resources".length);

	  this.discoveryUrlList.push(discoveryUrl);
	  this.discoveryUrlList.push(baseDiscoveryUrl);
	  this.discoveryUrlList.push(baseDiscoveryUrl + "/resources.json");
	  this.discoveryUrlList.push(baseDiscoveryUrl + "/resources");

	  log("Will try the following urls to discover api endpoints:")
	  for(var i = 0; i < this.discoveryUrlList.length; i++)
		log(" > " + this.discoveryUrlList[i]);

	  this.fetchEndpointsSeq();
    },

    fetchEndpointsSeq: function() {
      var controller = this;

	  if(this.discoveryUrlListCursor < this.discoveryUrlList.length) {
		  var url = this.discoveryUrlList[this.discoveryUrlListCursor++]
	      updateStatus("Fetching API List from " + url);
		  log("Trying url " + url);
	      $.getJSON(url + apiKeySuffix, function(response) {
	      })
	      .success(function(response) {
		      globalBasePath = url.substr(0, url.lastIndexOf("/"));
		      log("Setting globalBasePath to " + globalBasePath);
		      ApiResource.createAll(response.apis);
	          controller.fetchResources(response.basePath);
	      })
	   	  .error(function(response) {
	          controller.fetchEndpointsSeq();
		  });
	  } else {
	      log ('Error with resource discovery. Exhaused all possible endpoint urls');

		  var urlsTried = "";
		  for(var i = 0; i < this.discoveryUrlList.length; i++) {
			urlsTried = urlsTried + "<br/>" + this.discoveryUrlList[i];
		  }

	      updateStatus("Unable to fetch API Listing. Tried the following urls:" + urlsTried);
	  }
    },

    fetchResources: function(basePath) {
      log("fetchResources: basePath = " + basePath);
      //ApiResource.logAll();
      for (var i = 0; i < ApiResource.all().length; i++) {
        var apiResource = ApiResource.all()[i];
        this.fetchResource(apiResource);
      }
    },

    fetchResource: function(apiResource) {
      var controller = this;
      updateStatus("Fetching " + apiResource.name + "...");
      var resourceUrl = globalBasePath + apiResource.path_json + apiKeySuffix;
      // log("resourceUrl: %o", resourceUrl);
      $.getJSON(resourceUrl,
      function(response) {
        controller.loadResources(response, apiResource);
      });
    },

    loadResources: function(response, apiResource) {
      try {
        this.countLoaded++;
        //    log(response);
        // if (response.apis) {
          apiResource.addApis(response.apis);
        // }
        //        updateStatus("Parsed Apis");
        //log(response.models);
        if (response.models) {
          // log("response.models.length = " + response.models.length);
          for (var modeName in response.models) {
            var m = response.models[modeName];
            // log("creating " + m.id);
            apiResource.addModel(m);
            //      apiResource.modelList.create(m);
          }
        }

        updateStatus();
      } finally {
        if (this.countLoaded == ApiResource.count()) {
          // log("all models/api loaded");
          ApiResource.trigger("refresh");
        }
      }
    }

  });

  this.init = function() {
    this.modelController = ModelController.init();
  };
};

<|endoftext|>