<commit_before>var _ = require('underscore');
var Backbone = require('backbone');
var cdb = require('cartodb.js');

function getQueryForGeometryType (geometryType) {
  var QUERY = [
    'select tag.name, to_json(ARRAY(',
    '  select row_to_json(',
    '    (select row(_) from (select o.name, o.id, array_agg(',
    '      (select row(__) from (select tt.name, tt.id) as __)',
    '    ) as columns) as _)',
    '  )',
    '  from obs_tag o ',
    '  join obs_column_tag t on t.tag_id = o.id and t.column_id in (select ttt.column_id from obs_column_tag ttt where ttt.tag_id = tag.id)',
    '  join obs_column tt on tt.id = t.column_id',
    "  where o.name != 'Boundaries'"
    "    and o.type = 'subsection'",
    '    and tt.weight > 0 ',
    '    {{aggregate_condition}}',
    '  group by o.name, o.id',
    ')) as subsection',
    "from obs_tag tag where type = 'section'"
  ].join('\n');

  var replacement = '';
  if (geometryType === 'polygon') {
    replacement = "and tt.aggregate ILIKE 'sum'";
  }
  return QUERY.replace('{{aggregate_condition}}', replacement);
}

/**
 * this class fetches information from observatory metadata
 */
module.exports = Backbone.Model.extend({
  initialize: function (attrs, opts) {
    this._configModel = opts.configModel;
    this._geometryType = opts.geometryType;
  },

  sync: function (method, model, options) {
    // TODO: use configuration for data observatory
    var sql = cdb.SQL({ user: 'observatory' });
    sql.execute(getQueryForGeometryType(this._geometryType)).done(function (data) {
      options.success && options.success(data);
    }).error(function (errors) {
      // console.log("errors:" + errors);
    });
  },

  setGeometryType: function (geometryType) {
    this._geometryType = geometryType;
  },

  parse: function (attrs) {
    var a = {};
    _.each(attrs.rows, function (r) {
      var sub = {};
      _.each(r.subsection, function (s) {
        var cols = {};
        _.each(s.f1.columns, function (c) {
          cols[c.f1.name] = c.f1.id;
        });
        sub[s.f1.name] = cols;
      });
      a[r.name] = sub;
    });
    return a;
  },

  measurement: function (area) {
    var subsection = this.get(area);
    if (subsection) {
      return _.keys(subsection);
    }
    return [];
  },

  columns: function (area, subsectionName) {
    var subsection = this.get(area);
    if (subsection) {
      return _.keys(subsection[subsectionName] || {});
    }
    return [];
  },

  segment: function (area, subsectionName, column) {
    var subsection = this.get(area);
    if (subsection) {
      return subsection[subsectionName][column];
    }
    return null;
  },

  areas: function () {
    return _.keys(this.attributes);
  }
});
<commit_msg>Adds missing comma<commit_after>var _ = require('underscore');
var Backbone = require('backbone');
var cdb = require('cartodb.js');

function getQueryForGeometryType (geometryType) {
  var QUERY = [
    'select tag.name, to_json(ARRAY(',
    '  select row_to_json(',
    '    (select row(_) from (select o.name, o.id, array_agg(',
    '      (select row(__) from (select tt.name, tt.id) as __)',
    '    ) as columns) as _)',
    '  )',
    '  from obs_tag o ',
    '  join obs_column_tag t on t.tag_id = o.id and t.column_id in (select ttt.column_id from obs_column_tag ttt where ttt.tag_id = tag.id)',
    '  join obs_column tt on tt.id = t.column_id',
    "  where o.name != 'Boundaries'",
    "    and o.type = 'subsection'",
    '    and tt.weight > 0 ',
    '    {{aggregate_condition}}',
    '  group by o.name, o.id',
    ')) as subsection',
    "from obs_tag tag where type = 'section'"
  ].join('\n');

  var replacement = '';
  if (geometryType === 'polygon') {
    replacement = "and tt.aggregate ILIKE 'sum'";
  }
  return QUERY.replace('{{aggregate_condition}}', replacement);
}

/**
 * this class fetches information from observatory metadata
 */
module.exports = Backbone.Model.extend({
  initialize: function (attrs, opts) {
    this._configModel = opts.configModel;
    this._geometryType = opts.geometryType;
  },

  sync: function (method, model, options) {
    // TODO: use configuration for data observatory
    var sql = cdb.SQL({ user: 'observatory' });
    sql.execute(getQueryForGeometryType(this._geometryType)).done(function (data) {
      options.success && options.success(data);
    }).error(function (errors) {
      // console.log("errors:" + errors);
    });
  },

  setGeometryType: function (geometryType) {
    this._geometryType = geometryType;
  },

  parse: function (attrs) {
    var a = {};
    _.each(attrs.rows, function (r) {
      var sub = {};
      _.each(r.subsection, function (s) {
        var cols = {};
        _.each(s.f1.columns, function (c) {
          cols[c.f1.name] = c.f1.id;
        });
        sub[s.f1.name] = cols;
      });
      a[r.name] = sub;
    });
    return a;
  },

  measurement: function (area) {
    var subsection = this.get(area);
    if (subsection) {
      return _.keys(subsection);
    }
    return [];
  },

  columns: function (area, subsectionName) {
    var subsection = this.get(area);
    if (subsection) {
      return _.keys(subsection[subsectionName] || {});
    }
    return [];
  },

  segment: function (area, subsectionName, column) {
    var subsection = this.get(area);
    if (subsection) {
      return subsection[subsectionName][column];
    }
    return null;
  },

  areas: function () {
    return _.keys(this.attributes);
  }
});
<|endoftext|>


<commit_before>var Bind = require("github/jillix/bind");
var Events = require("github/jillix/events");

module.exports = function(config) {

    // get self (the module)
    var self = this;

    // config
    self.config = config;

    // process config
    processConfig(config);

    // call events
    Events.call(self, config);

    // get the iframe from the module
    var $iframe = $("iframe", self.dom);

    // get file inputs
    var $fileInputs = $("input[type='file']", self.dom);

    // create the accept attr value
    var acceptAttrValue = "";

    // for every file type
    for (var i = 0; i < self.config.options.acceptTypes.length; ++i) {

        // get current type
        var cType = self.config.options.acceptTypes[i];

        // append it to the attr value string
        acceptAttrValue += "." + cType + ",";
    }

    // remove last comma
    acceptAttrValue = acceptAttrValue.substring(0, acceptAttrValue.length - 1);

    // set accept attr
    $fileInputs.attr("accept", acceptAttrValue);

    // file input change event
    $fileInputs.on("change", function () {
        // is the file accepted?
        if (acceptAttrValue && !checkFileType.call(self, this)) {
            // empty the value of file input
            // TODO is this supported on IE?
            $(this).val("");
        }
    });

    // forms on submit
    $("form", self.dom).on("submit", function () {

        // get submiited form
        var $form = $(this);

        // unset the load handler
        $iframe.off("load");

        // set it again
        $iframe.on("load", function () {

            // get text from the page
            var result = $(this.contentWindow.document).text();

            // if no result, return
            if (!result) { return; }

            // parse the result
            try {
                result = JSON.parse(result);
            } catch (e) {
                console.log(e);
                return;
            }

            if (result.error || !result.success) { return; }

            // emit fileUploaded event and the result
            self.emit("fileUploaded", result.success);

            // get file input jQuery object
            var $inputFile = $form.find("input[type='file']");

            // verify its value
            if (!$inputFile.val()) { return; }

            // set src of iframe
            $iframe.attr("src", "");

            // replace the file input
            $inputFile.replaceWith($inputFile.clone(true));
        });
    });

    // emit ready event
    self.emit("ready", config);

    // listen to events
    self.on("setData", setData);
    self.on("removeItem", removeItem);
};

/*
 * This function makes a request to the remove operation to delete a file
 * */
function removeItem (itemId) {
    var self = this;

    // make the request
    var data = {
        itemId: itemId
    }
    self.link('remove', { data: data }, function (err, data) {

        if (err) {
            return;
        }

        self.emit('itemRemoved');
    });
}

/*
 *  This function checks if the selected file has a correct
 *  extension (self.config.options.acceptTypes)
 * */
function checkFileType (fileInput) {
    // get self
    var self = this;

    // get file extension
    var extension = fileInput.value.split(".").reverse()[0];

    // the extension is not supported
    if (self.config.options.acceptTypes.indexOf(extension) === -1) {
        return false;
    }

    // the extension is supported
    return true;
}

/*
 *  This function appends to the upload form hidden inputs with values
 *  from the selected table item
 * */
function setData (data, path) {

    var self = this;

    if (!path) {
        path = "";
    }

    // build the inputs from the object
    for (var key in data) {
        if (key[0] !== '_' && data.hasOwnProperty(key)) {

            // build the object path
            var objPath = path.length ? (path + '.' + key) : key;

            if (typeof data[key] === "object") {

                setData(data[key], objPath);
            } else {

                // get the form
                var $form = $("form", self.dom);

                //create the input
                var el = '<input type="hidden" class="hide" name="' + objPath + '" value="' + data[key] + '">';
                $form.append(el);
            }
        }
    }
}

/*
 *  Process config
 *
 * */
function processConfig (config) {
    config.options = config.options || {};
    config.options.acceptTypes = config.options.acceptTypes || [];
}
<commit_msg>removed console.log<commit_after>var Bind = require("github/jillix/bind");
var Events = require("github/jillix/events");

module.exports = function(config) {

    // get self (the module)
    var self = this;

    // config
    self.config = config;

    // process config
    processConfig(config);

    // call events
    Events.call(self, config);

    // get the iframe from the module
    var $iframe = $("iframe", self.dom);

    // get file inputs
    var $fileInputs = $("input[type='file']", self.dom);

    // create the accept attr value
    var acceptAttrValue = "";

    // for every file type
    for (var i = 0; i < self.config.options.acceptTypes.length; ++i) {

        // get current type
        var cType = self.config.options.acceptTypes[i];

        // append it to the attr value string
        acceptAttrValue += "." + cType + ",";
    }

    // remove last comma
    acceptAttrValue = acceptAttrValue.substring(0, acceptAttrValue.length - 1);

    // set accept attr
    $fileInputs.attr("accept", acceptAttrValue);

    // file input change event
    $fileInputs.on("change", function () {
        // is the file accepted?
        if (acceptAttrValue && !checkFileType.call(self, this)) {
            // empty the value of file input
            // TODO is this supported on IE?
            $(this).val("");
        }
    });

    // forms on submit
    $("form", self.dom).on("submit", function () {

        // get submiited form
        var $form = $(this);

        // unset the load handler
        $iframe.off("load");

        // set it again
        $iframe.on("load", function () {

            // get text from the page
            var result = $(this.contentWindow.document).text();

            // if no result, return
            if (!result) { return; }

            // parse the result
            try {
                result = JSON.parse(result);
            } catch (e) {
                return;
            }

            if (result.error || !result.success) { return; }

            // emit fileUploaded event and the result
            self.emit("fileUploaded", result.success);

            // get file input jQuery object
            var $inputFile = $form.find("input[type='file']");

            // verify its value
            if (!$inputFile.val()) { return; }

            // set src of iframe
            $iframe.attr("src", "");

            // replace the file input
            $inputFile.replaceWith($inputFile.clone(true));
        });
    });

    // emit ready event
    self.emit("ready", config);

    // listen to events
    self.on("setData", setData);
    self.on("removeItem", removeItem);
};

/*
 * This function makes a request to the remove operation to delete a file
 * */
function removeItem (itemId) {
    var self = this;

    // make the request
    var data = {
        itemId: itemId
    }
    self.link('remove', { data: data }, function (err, data) {

        if (err) {
            return;
        }

        self.emit('itemRemoved');
    });
}

/*
 *  This function checks if the selected file has a correct
 *  extension (self.config.options.acceptTypes)
 * */
function checkFileType (fileInput) {
    // get self
    var self = this;

    // get file extension
    var extension = fileInput.value.split(".").reverse()[0];

    // the extension is not supported
    if (self.config.options.acceptTypes.indexOf(extension) === -1) {
        return false;
    }

    // the extension is supported
    return true;
}

/*
 *  This function appends to the upload form hidden inputs with values
 *  from the selected table item
 * */
function setData (data, path) {

    var self = this;

    if (!path) {
        path = "";
    }

    // build the inputs from the object
    for (var key in data) {
        if (key[0] !== '_' && data.hasOwnProperty(key)) {

            // build the object path
            var objPath = path.length ? (path + '.' + key) : key;

            if (typeof data[key] === "object") {

                setData(data[key], objPath);
            } else {

                // get the form
                var $form = $("form", self.dom);

                //create the input
                var el = '<input type="hidden" class="hide" name="' + objPath + '" value="' + data[key] + '">';
                $form.append(el);
            }
        }
    }
}

/*
 *  Process config
 *
 * */
function processConfig (config) {
    config.options = config.options || {};
    config.options.acceptTypes = config.options.acceptTypes || [];
}
<|endoftext|>


<commit_before>odoo.define('web.Menu', function (require) {
"use strict";

var core = require('web.core');
var session = require('web.session');
var Widget = require('web.Widget');

var QWeb = core.qweb;

var Menu = Widget.extend({
    init: function() {
        var self = this;
        this._super.apply(this, arguments);
        this.is_bound = $.Deferred();
        this.data = {data:{children:[]}};
        this.on("menu_bound", this, function() {
            // launch the fetch of needaction counters, asynchronous
            var $all_menus = self.$el.parents('.o_web_client').find('.o_sub_menu').find('[data-menu]');
            var all_menu_ids = _.map($all_menus, function (menu) {return parseInt($(menu).attr('data-menu'), 10);});
            if (!_.isEmpty(all_menu_ids)) {
                this.do_load_needaction(all_menu_ids);
            }
        });
        core.bus.on('do_reload_needaction', this, this.do_reload_needaction);
        core.bus.on('change_menu_section', this, this.on_change_top_menu);
    },
    start: function() {
        this._super.apply(this, arguments);
        return this.bind_menu();
    },
    do_reload: function() {
        var self = this;
        self.bind_menu();
    },
    bind_menu: function() {
        var self = this;
        this.$secondary_menus = this.$el.parents().find('.o_sub_menu');
        this.$secondary_menus.on('click', 'a[data-menu]', this.on_menu_click);
        this.$el.on('click', 'a[data-menu]', function (event) {
            event.preventDefault();
            var menu_id = $(event.currentTarget).data('menu');
            var needaction = $(event.target).is('div#menu_counter');
            core.bus.trigger('change_menu_section', menu_id, needaction);
        });

        // Hide second level submenus
        this.$secondary_menus.find('.oe_menu_toggler').siblings('.oe_secondary_submenu').addClass('o_hidden');
        if (self.current_menu) {
            self.open_menu(self.current_menu);
        }
        this.trigger('menu_bound');

        var lazyreflow = _.debounce(this.reflow.bind(this), 200);
        core.bus.on('resize', this, function() {
            if ($(window).width() < 768 ) {
                lazyreflow('all_outside');
            } else {
                lazyreflow();
            }
        });
        core.bus.trigger('resize');

        this.is_bound.resolve();
    },
    do_load_needaction: function (menu_ids) {
        var self = this;
        menu_ids = _.compact(menu_ids);
        if (_.isEmpty(menu_ids)) {
            return $.when();
        }
        return this.rpc("/web/menu/load_needaction", {'menu_ids': menu_ids}).done(function(r) {
            self.on_needaction_loaded(r);
        });
    },
    on_needaction_loaded: function(data) {
        var self = this;
        this.needaction_data = data;
        _.each(this.needaction_data, function (item, menu_id) {
            var $item = self.$secondary_menus.find('a[data-menu="' + menu_id + '"]');
            $item.find('.badge').remove();
            if (item.needaction_counter && item.needaction_counter > 0) {
                $item.append(QWeb.render("Menu.needaction_counter", { widget : item }));
            }
        });
    },
    /**
     * Reflow the menu items and dock overflowing items into a "More" menu item.
     * Automatically called when 'menu_bound' event is triggered and on window resizing.
     *
     * @param {string} behavior If set to 'all_outside', all the items are displayed.
     * If not set, only the overflowing items are hidden.
     */
    reflow: function(behavior) {
        var self = this;
        var $more_container = this.$('#menu_more_container').hide();
        var $more = this.$('#menu_more');
        var $systray = this.$el.parents().find('.oe_systray');

        $more.children('li').insertBefore($more_container);  // Pull all the items out of the more menu

        // 'all_outside' beahavior should display all the items, so hide the more menu and exit
        if (behavior === 'all_outside') {
            // Show list of menu items
            self.$el.show();
            this.$el.find('li').show();
            $more_container.hide();
            return;
        }

        // Hide all menu items
        var $toplevel_items = this.$el.find('li').not($more_container).not($systray.find('li')).hide();
        // Show list of menu items (which is empty for now since all menu items are hidden)
        self.$el.show();
        $toplevel_items.each(function() {
            var remaining_space = self.$el.parent().width() - $more_container.outerWidth();
            self.$el.parent().children(':visible').each(function() {
                remaining_space -= $(this).outerWidth();
            });

            if ($(this).width() >= remaining_space) {
                return false; // the current item will be appended in more_container
            }
            $(this).show(); // show the current item in menu bar
        });
        $more.append($toplevel_items.filter(':hidden').show());
        $more_container.toggle(!!$more.children().length);
        // Hide toplevel item if there is only one
        var $toplevel = self.$el.children("li:visible");
        if ($toplevel.length === 1) {
            $toplevel.hide();
        }
    },
    /**
     * Opens a given menu by id, as if a user had browsed to that menu by hand
     * except does not trigger any event on the way
     *
     * @param {Number} id database id of the terminal menu to select
     */
    open_menu: function (id) {
        this.current_menu = id;
        session.active_id = id;
        var $clicked_menu, $sub_menu, $main_menu;
        $clicked_menu = this.$el.add(this.$secondary_menus).find('a[data-menu=' + id + ']');
        this.trigger('open_menu', id, $clicked_menu);

        if (this.$secondary_menus.has($clicked_menu).length) {
            $sub_menu = $clicked_menu.parents('.oe_secondary_menu');
            $main_menu = this.$el.find('a[data-menu=' + $sub_menu.data('menu-parent') + ']');
        } else {
            $sub_menu = this.$secondary_menus.find('.oe_secondary_menu[data-menu-parent=' + $clicked_menu.attr('data-menu') + ']');
            $main_menu = $clicked_menu;
        }

        // Activate current main menu
        this.$el.find('.active').removeClass('active');
        $main_menu.parent().addClass('active');

        // Show current sub menu
        this.$secondary_menus.find('.oe_secondary_menu').hide();
        $sub_menu.show();

        // Hide/Show the leftbar menu depending of the presence of sub-items
        this.$secondary_menus.toggleClass('o_hidden', !$sub_menu.children().length);

        // Activate current menu item and show parents
        this.$secondary_menus.find('.active').removeClass('active');
        if ($main_menu !== $clicked_menu) {
            $clicked_menu.parents().removeClass('o_hidden');
            if ($clicked_menu.is('.oe_menu_toggler')) {
                $clicked_menu.toggleClass('oe_menu_opened').siblings('.oe_secondary_submenu:first').toggle();
            } else {
                $clicked_menu.parent().addClass('active');
            }
        }
        // add a tooltip to cropped menu items
        this.$secondary_menus.find('.oe_secondary_submenu li a span').each(function() {
            $(this).tooltip(this.scrollWidth > this.clientWidth ? {title: $(this).text().trim(), placement: 'right'} :'destroy');
       });
    },
    /**
     * Call open_menu with the first menu_item matching an action_id
     *
     * @param {Number} id the action_id to match
     */
    open_action: function (id) {
        var $menu = this.$el.add(this.$secondary_menus).find('a[data-action-id="' + id + '"]');
        var menu_id = $menu.data('menu');
        if (menu_id) {
            this.open_menu(menu_id);
        }
    },
    /**
     * Process a click on a menu item
     *
     * @param {Number} id the menu_id
     * @param {Boolean} [needaction=false] whether the triggered action should execute in a `needs action` context
     */
    menu_click: function(id, needaction) {
        if (!id) { return; }

        // find back the menuitem in dom to get the action
        var $item = this.$el.find('a[data-menu=' + id + ']');
        if (!$item.length) {
            $item = this.$secondary_menus.find('a[data-menu=' + id + ']');
        }
        var action_id = $item.data('action-id');
        // If first level menu doesnt have action trigger first leaf
        if (!action_id) {
            if(this.$el.has($item).length) {
                var $sub_menu = this.$secondary_menus.find('.oe_secondary_menu[data-menu-parent=' + id + ']');
                var $items = $sub_menu.find('a[data-action-id]').filter('[data-action-id!=""]');
                if($items.length) {
                    action_id = $items.data('action-id');
                    id = $items.data('menu');
                }
            }
        }
        if (action_id) {
            this.trigger('menu_click', {
                action_id: action_id,
                needaction: needaction,
                id: id,
                previous_menu_id: this.current_menu // Here we don't know if action will fail (in which case we have to revert menu)
            }, $item);
        } else {
            console.log('Menu no action found web test 04 will fail');
        }
        this.open_menu(id);
    },
    do_reload_needaction: function () {
        var self = this;
        if (self.current_menu) {
            self.do_load_needaction([self.current_menu]).then(function () {
                self.trigger("need_action_reloaded");
            });
        }
    },
    /**
     * Change the current top menu
     *
     * @param {int} [menu_id] the top menu id
     * @param {boolean} [needaction] true to redirect to menu's needactions
     */
    on_change_top_menu: function(menu_id, needaction) {
        var self = this;
        // Fetch the menu leaves ids in order to check if they need a 'needaction'
        var $secondary_menu = this.$el.parents().find('.oe_secondary_menu[data-menu-parent=' + menu_id + ']');
        var $menu_leaves = $secondary_menu.children().find('.oe_menu_leaf');
        var menu_ids = _.map($menu_leaves, function (leave) {return parseInt($(leave).attr('data-menu'), 10);});

        self.do_load_needaction(menu_ids).then(function () {
            self.trigger("need_action_reloaded");
        });
        this.$el.parents().find(".oe_secondary_menus_container").scrollTop(0,0);

        this.menu_click(menu_id, needaction);
    },
    on_menu_click: function(ev) {
        ev.preventDefault();
        var needaction = $(ev.target).is('div#menu_counter');
        this.menu_click($(ev.currentTarget).data('menu'), needaction);
    },
});

return Menu;
});
<commit_msg>[FIX] web: submenu need toggling of .o_hidden<commit_after>odoo.define('web.Menu', function (require) {
"use strict";

var core = require('web.core');
var session = require('web.session');
var Widget = require('web.Widget');

var QWeb = core.qweb;

var Menu = Widget.extend({
    init: function() {
        var self = this;
        this._super.apply(this, arguments);
        this.is_bound = $.Deferred();
        this.data = {data:{children:[]}};
        this.on("menu_bound", this, function() {
            // launch the fetch of needaction counters, asynchronous
            var $all_menus = self.$el.parents('.o_web_client').find('.o_sub_menu').find('[data-menu]');
            var all_menu_ids = _.map($all_menus, function (menu) {return parseInt($(menu).attr('data-menu'), 10);});
            if (!_.isEmpty(all_menu_ids)) {
                this.do_load_needaction(all_menu_ids);
            }
        });
        core.bus.on('do_reload_needaction', this, this.do_reload_needaction);
        core.bus.on('change_menu_section', this, this.on_change_top_menu);
    },
    start: function() {
        this._super.apply(this, arguments);
        return this.bind_menu();
    },
    do_reload: function() {
        var self = this;
        self.bind_menu();
    },
    bind_menu: function() {
        var self = this;
        this.$secondary_menus = this.$el.parents().find('.o_sub_menu');
        this.$secondary_menus.on('click', 'a[data-menu]', this.on_menu_click);
        this.$el.on('click', 'a[data-menu]', function (event) {
            event.preventDefault();
            var menu_id = $(event.currentTarget).data('menu');
            var needaction = $(event.target).is('div#menu_counter');
            core.bus.trigger('change_menu_section', menu_id, needaction);
        });

        // Hide second level submenus
        this.$secondary_menus.find('.oe_menu_toggler').siblings('.oe_secondary_submenu').addClass('o_hidden');
        if (self.current_menu) {
            self.open_menu(self.current_menu);
        }
        this.trigger('menu_bound');

        var lazyreflow = _.debounce(this.reflow.bind(this), 200);
        core.bus.on('resize', this, function() {
            if ($(window).width() < 768 ) {
                lazyreflow('all_outside');
            } else {
                lazyreflow();
            }
        });
        core.bus.trigger('resize');

        this.is_bound.resolve();
    },
    do_load_needaction: function (menu_ids) {
        var self = this;
        menu_ids = _.compact(menu_ids);
        if (_.isEmpty(menu_ids)) {
            return $.when();
        }
        return this.rpc("/web/menu/load_needaction", {'menu_ids': menu_ids}).done(function(r) {
            self.on_needaction_loaded(r);
        });
    },
    on_needaction_loaded: function(data) {
        var self = this;
        this.needaction_data = data;
        _.each(this.needaction_data, function (item, menu_id) {
            var $item = self.$secondary_menus.find('a[data-menu="' + menu_id + '"]');
            $item.find('.badge').remove();
            if (item.needaction_counter && item.needaction_counter > 0) {
                $item.append(QWeb.render("Menu.needaction_counter", { widget : item }));
            }
        });
    },
    /**
     * Reflow the menu items and dock overflowing items into a "More" menu item.
     * Automatically called when 'menu_bound' event is triggered and on window resizing.
     *
     * @param {string} behavior If set to 'all_outside', all the items are displayed.
     * If not set, only the overflowing items are hidden.
     */
    reflow: function(behavior) {
        var self = this;
        var $more_container = this.$('#menu_more_container').hide();
        var $more = this.$('#menu_more');
        var $systray = this.$el.parents().find('.oe_systray');

        $more.children('li').insertBefore($more_container);  // Pull all the items out of the more menu

        // 'all_outside' beahavior should display all the items, so hide the more menu and exit
        if (behavior === 'all_outside') {
            // Show list of menu items
            self.$el.show();
            this.$el.find('li').show();
            $more_container.hide();
            return;
        }

        // Hide all menu items
        var $toplevel_items = this.$el.find('li').not($more_container).not($systray.find('li')).hide();
        // Show list of menu items (which is empty for now since all menu items are hidden)
        self.$el.show();
        $toplevel_items.each(function() {
            var remaining_space = self.$el.parent().width() - $more_container.outerWidth();
            self.$el.parent().children(':visible').each(function() {
                remaining_space -= $(this).outerWidth();
            });

            if ($(this).width() >= remaining_space) {
                return false; // the current item will be appended in more_container
            }
            $(this).show(); // show the current item in menu bar
        });
        $more.append($toplevel_items.filter(':hidden').show());
        $more_container.toggle(!!$more.children().length);
        // Hide toplevel item if there is only one
        var $toplevel = self.$el.children("li:visible");
        if ($toplevel.length === 1) {
            $toplevel.hide();
        }
    },
    /**
     * Opens a given menu by id, as if a user had browsed to that menu by hand
     * except does not trigger any event on the way
     *
     * @param {Number} id database id of the terminal menu to select
     */
    open_menu: function (id) {
        this.current_menu = id;
        session.active_id = id;
        var $clicked_menu, $sub_menu, $main_menu;
        $clicked_menu = this.$el.add(this.$secondary_menus).find('a[data-menu=' + id + ']');
        this.trigger('open_menu', id, $clicked_menu);

        if (this.$secondary_menus.has($clicked_menu).length) {
            $sub_menu = $clicked_menu.parents('.oe_secondary_menu');
            $main_menu = this.$el.find('a[data-menu=' + $sub_menu.data('menu-parent') + ']');
        } else {
            $sub_menu = this.$secondary_menus.find('.oe_secondary_menu[data-menu-parent=' + $clicked_menu.attr('data-menu') + ']');
            $main_menu = $clicked_menu;
        }

        // Activate current main menu
        this.$el.find('.active').removeClass('active');
        $main_menu.parent().addClass('active');

        // Show current sub menu
        this.$secondary_menus.find('.oe_secondary_menu').hide();
        $sub_menu.show();

        // Hide/Show the leftbar menu depending of the presence of sub-items
        this.$secondary_menus.toggleClass('o_hidden', !$sub_menu.children().length);

        // Activate current menu item and show parents
        this.$secondary_menus.find('.active').removeClass('active');
        if ($main_menu !== $clicked_menu) {
            $clicked_menu.parents().removeClass('o_hidden');
            if ($clicked_menu.is('.oe_menu_toggler')) {
                $clicked_menu.toggleClass('oe_menu_opened').siblings('.oe_secondary_submenu:first').toggleClass('o_hidden');
            } else {
                $clicked_menu.parent().addClass('active');
            }
        }
        // add a tooltip to cropped menu items
        this.$secondary_menus.find('.oe_secondary_submenu li a span').each(function() {
            $(this).tooltip(this.scrollWidth > this.clientWidth ? {title: $(this).text().trim(), placement: 'right'} :'destroy');
       });
    },
    /**
     * Call open_menu with the first menu_item matching an action_id
     *
     * @param {Number} id the action_id to match
     */
    open_action: function (id) {
        var $menu = this.$el.add(this.$secondary_menus).find('a[data-action-id="' + id + '"]');
        var menu_id = $menu.data('menu');
        if (menu_id) {
            this.open_menu(menu_id);
        }
    },
    /**
     * Process a click on a menu item
     *
     * @param {Number} id the menu_id
     * @param {Boolean} [needaction=false] whether the triggered action should execute in a `needs action` context
     */
    menu_click: function(id, needaction) {
        if (!id) { return; }

        // find back the menuitem in dom to get the action
        var $item = this.$el.find('a[data-menu=' + id + ']');
        if (!$item.length) {
            $item = this.$secondary_menus.find('a[data-menu=' + id + ']');
        }
        var action_id = $item.data('action-id');
        // If first level menu doesnt have action trigger first leaf
        if (!action_id) {
            if(this.$el.has($item).length) {
                var $sub_menu = this.$secondary_menus.find('.oe_secondary_menu[data-menu-parent=' + id + ']');
                var $items = $sub_menu.find('a[data-action-id]').filter('[data-action-id!=""]');
                if($items.length) {
                    action_id = $items.data('action-id');
                    id = $items.data('menu');
                }
            }
        }
        if (action_id) {
            this.trigger('menu_click', {
                action_id: action_id,
                needaction: needaction,
                id: id,
                previous_menu_id: this.current_menu // Here we don't know if action will fail (in which case we have to revert menu)
            }, $item);
        } else {
            console.log('Menu no action found web test 04 will fail');
        }
        this.open_menu(id);
    },
    do_reload_needaction: function () {
        var self = this;
        if (self.current_menu) {
            self.do_load_needaction([self.current_menu]).then(function () {
                self.trigger("need_action_reloaded");
            });
        }
    },
    /**
     * Change the current top menu
     *
     * @param {int} [menu_id] the top menu id
     * @param {boolean} [needaction] true to redirect to menu's needactions
     */
    on_change_top_menu: function(menu_id, needaction) {
        var self = this;
        // Fetch the menu leaves ids in order to check if they need a 'needaction'
        var $secondary_menu = this.$el.parents().find('.oe_secondary_menu[data-menu-parent=' + menu_id + ']');
        var $menu_leaves = $secondary_menu.children().find('.oe_menu_leaf');
        var menu_ids = _.map($menu_leaves, function (leave) {return parseInt($(leave).attr('data-menu'), 10);});

        self.do_load_needaction(menu_ids).then(function () {
            self.trigger("need_action_reloaded");
        });
        this.$el.parents().find(".oe_secondary_menus_container").scrollTop(0,0);

        this.menu_click(menu_id, needaction);
    },
    on_menu_click: function(ev) {
        ev.preventDefault();
        var needaction = $(ev.target).is('div#menu_counter');
        this.menu_click($(ev.currentTarget).data('menu'), needaction);
    },
});

return Menu;
});
<|endoftext|>


<commit_before>'use strict';

const trafficShapeParser = require('./trafficShapeParser'),
  Promise = require('bluebird'),
  execa = require('execa'),
  path = require('path'),
  sltc = require('sltc').sltc,
  log = require('intel');

const SCRIPT_PATH = path.join(__dirname, '..', '..', 'vendor', 'tsproxy.py');

function getStartupCriteriaListener(resolve, reject) {
  return function(data) {
    const logLine = data.toString();
    if (logLine.startsWith('Started Socks5 proxy server on')) {
      log.debug('Started TSproxy');
      return resolve();
    } else if (logLine.startsWith('Unable to listen on')) {
      return reject(new Error('TSProxy process logged: ' + logLine));
    }
  }
}

class TSProxyLauncher {
  constructor(options) {
    this.options = options;
  }

  start(profile) {
    const scriptArgs = [SCRIPT_PATH, '--rtt', profile.latency, '--inkbps', profile.downstreamKbps, '--outkbps', profile.upstreamKbps, '-p', this.options.connectivity.tsproxy.port];

    if (this.options.v >= 2)  {
      scriptArgs.push('-vvvv');
    }

    log.info('Start tsproxy:' + scriptArgs.join(' '));
    this.tsProxyProcess = execa('python', scriptArgs);

    const tsProxyProcess = this.tsProxyProcess;

    if (this.options.v >= 2) {
      tsProxyProcess.stderr.on('data', function(data) {
        log.verbose(data.toString())
      })
    }

    return new Promise(function(resolve, reject) {
      tsProxyProcess.stdout
        .on('data', getStartupCriteriaListener(resolve, reject));
    });

  }

  stop() {
    if (this.tsProxyProcess) {
      this.tsProxyProcess.stdout.removeAllListeners('data');
      this.tsProxyProcess.stderr.removeAllListeners('data');

      return new Promise((resolve, reject) => {
        this.tsProxyProcess.once('exit', () => {
          // guard against exit event sent after error event,
          // see https://nodejs.org/api/child_process.html#child_process_event_error
          if (this.tsProxyProcess) {
            this.tsProxyProcess = null;
            log.debug('Closing TSproxy')
            resolve();
          }
        }).once('error', (err) => {
          this.tsProxyProcess = null;
          reject(err);
        });
        this.tsProxyProcess.kill('SIGINT');
      });
    }
    return Promise.resolve();
  }
}

module.exports = {
  set: function(options) {
    const profile = trafficShapeParser.parseTrafficShapeConfig(options);
    if (profile !== null) {
      if (options.connectivity && options.connectivity.engine && options.connectivity.engine === 'tc') {
      // SLTC only support setting same up/down to work in Docker
      return new Promise(function(resolve) {
      sltc({
          device: options.connectivity.tc ? options.connectivity.tc.device : 'eth0',
          bandwidth: profile.downstreamKbps + 'kbps',
          latency: profile.latency + 'ms',
          pl: '0%'
        });
        resolve();
      });

      } else if (options.connectivity && options.connectivity.engine && options.connectivity.engine === 'tsproxy') {
        this.TSProxyLauncher = new TSProxyLauncher(options);
        return this.TSProxyLauncher.start(profile);
      }
    }
  },
  remove: function(options) {
    if (options.connectivity && options.connectivity.engine && options.connectivity.engine === 'tc') {
      return new Promise(function(resolve) {
        sltc({
        device: options.connectivity.device,
        remove: true
      });
      resolve();
    });
    } else if (options.connectivity && options.connectivity.engine && options.connectivity.engine === 'tsproxy') {
      return this.TSProxyLauncher.stop();
    }
  }
}
<commit_msg>Avoid double line breaks in log from tsProxy.<commit_after>'use strict';

const trafficShapeParser = require('./trafficShapeParser'),
  Promise = require('bluebird'),
  execa = require('execa'),
  path = require('path'),
  sltc = require('sltc').sltc,
  log = require('intel');

const SCRIPT_PATH = path.join(__dirname, '..', '..', 'vendor', 'tsproxy.py');

function getStartupCriteriaListener(resolve, reject) {
  return function(data) {
    const logLine = data.toString();
    if (logLine.startsWith('Started Socks5 proxy server on')) {
      log.debug('Started TSproxy');
      return resolve();
    } else if (logLine.startsWith('Unable to listen on')) {
      return reject(new Error('TSProxy process logged: ' + logLine));
    }
  }
}

class TSProxyLauncher {
  constructor(options) {
    this.options = options;
  }

  start(profile) {
    const scriptArgs = [SCRIPT_PATH, '--rtt', profile.latency, '--inkbps', profile.downstreamKbps, '--outkbps', profile.upstreamKbps, '-p', this.options.connectivity.tsproxy.port];

    if (this.options.v >= 2)  {
      scriptArgs.push('-vvvv');
    }

    log.info('Start tsproxy:' + scriptArgs.join(' '));
    this.tsProxyProcess = execa('python', scriptArgs);

    const tsProxyProcess = this.tsProxyProcess;

    if (this.options.v >= 2) {
      tsProxyProcess.stderr.on('data', function(data) {
        log.verbose(data.toString().trim())
      })
    }

    return new Promise(function(resolve, reject) {
      tsProxyProcess.stdout
        .on('data', getStartupCriteriaListener(resolve, reject));
    });

  }

  stop() {
    if (this.tsProxyProcess) {
      this.tsProxyProcess.stdout.removeAllListeners('data');
      this.tsProxyProcess.stderr.removeAllListeners('data');

      return new Promise((resolve, reject) => {
        this.tsProxyProcess.once('exit', () => {
          // guard against exit event sent after error event,
          // see https://nodejs.org/api/child_process.html#child_process_event_error
          if (this.tsProxyProcess) {
            this.tsProxyProcess = null;
            log.debug('Closing TSproxy')
            resolve();
          }
        }).once('error', (err) => {
          this.tsProxyProcess = null;
          reject(err);
        });
        this.tsProxyProcess.kill('SIGINT');
      });
    }
    return Promise.resolve();
  }
}

module.exports = {
  set: function(options) {
    const profile = trafficShapeParser.parseTrafficShapeConfig(options);
    if (profile !== null) {
      if (options.connectivity && options.connectivity.engine && options.connectivity.engine === 'tc') {
      // SLTC only support setting same up/down to work in Docker
      return new Promise(function(resolve) {
      sltc({
          device: options.connectivity.tc ? options.connectivity.tc.device : 'eth0',
          bandwidth: profile.downstreamKbps + 'kbps',
          latency: profile.latency + 'ms',
          pl: '0%'
        });
        resolve();
      });

      } else if (options.connectivity && options.connectivity.engine && options.connectivity.engine === 'tsproxy') {
        this.TSProxyLauncher = new TSProxyLauncher(options);
        return this.TSProxyLauncher.start(profile);
      }
    }
  },
  remove: function(options) {
    if (options.connectivity && options.connectivity.engine && options.connectivity.engine === 'tc') {
      return new Promise(function(resolve) {
        sltc({
        device: options.connectivity.device,
        remove: true
      });
      resolve();
    });
    } else if (options.connectivity && options.connectivity.engine && options.connectivity.engine === 'tsproxy') {
      return this.TSProxyLauncher.stop();
    }
  }
}
<|endoftext|>


<commit_before>'use strict'
var colors = require('colors-browserify') //eslint-disable-line
// use level-js -- both browser and node thing!
var levelup = require('levelup')
var Event = require('vigour-js/lib/event')
var setWithPath = require('vigour-js/lib/util/setwithpath')
var leveljs = require('level-js')
var leveldown
try {
  leveldown = require('leveldown')
} catch (e) {
  console.error(e)
}
var Observable = require('vigour-js/lib/observable')
// need to install leveldown

// console.log('level it!'.rainbow)

// use same parse function -- see it as an upstream? dont store upstream no!
exports.properties = {
  // also do scopes
  levelready: new Observable({
    inject: require('vigour-js/lib/observable/is')
  }),
  levelup (val) {
    if (this.levelup) {
      console.log('destroy!')
    }
    if (!val) {
      val = this.get('adapter.id', 'hub')
    }
    var hub = this
    this.set({ levelready: false })

    var event = new Event(hub, 'level')
    event.isTriggered = true
    // correct path put in folder
    var db = this.levelup = levelup('level-' + val, { db: leveldown || leveljs })
    db.open(function onOpen () {
      var r = db.createReadStream()
      r.on('data', function (data) {
        // write to correct hub
        var obj = setWithPath({}, data.key.split('.'), data.value)
        hub = hub.set(obj, event) || hub
        // hub.get(data.key, {}).set(data.value, event)
        console.log(data.key, '=', data.value)
      })
      .on('error', function (err) {
        hub.levelready.val = true
        console.log('Oh my!', err)
      })
      .on('close', function () {
        hub.levelready.val = true
        console.log('Stream closed')
      })
      .on('end', function () {
        event.trigger()
        hub.levelready.val = true
        console.log('Stream closed')
      })
      console.log('level times!'.blue)
    })
  }
}

// make a queue object
exports.on = {
  data: {
    level (data, event) {
      if (event.type === 'level') {
        return
      }
      var db = this.lookUp('levelup') // until you find it
      // level db path
      if (db && this.syncPath[0] !== 'clients') {
        if (typeof data !== 'object') {
          db.put(this.syncPath.join('.'), data, () => {
            console.log('wrote to level--->'.blue, data)
          })
        } else if (data === null) {
          db.del(this.syncPath.join('.'), function () {
            console.log('remove from level--->'.red, data)
          })
        }
        // if data null -- also remove all bested fields!
      }
      // console.log('level >'.grey, this.path.join('.'), db)
    }
  }
}
<commit_msg>added level§<commit_after>'use strict'
var colors = require('colors-browserify') //eslint-disable-line
// use level-js -- both browser and node thing!
var levelup = require('levelup')
var Event = require('vigour-js/lib/event')
var setWithPath = require('vigour-js/lib/util/setwithpath')
var leveljs = require('level-js')
var leveldown
try {
  leveldown = require('leveldown')
} catch (e) {
  console.error(e)
}
var Observable = require('vigour-js/lib/observable')
// need to install leveldown

// console.log('level it!'.rainbow)

// use same parse function -- see it as an upstream? dont store upstream no!
exports.properties = {
  // also do scopes
  levelready: new Observable({
    inject: require('vigour-js/lib/observable/is')
  }),
  levelup (val) {
    if (this.levelup) {
      console.log('destroy!')
    }
    if (!val) {
      val = this.get('adapter.id', 'hub')
    }
    var hub = this
    this.set({ levelready: false })

    var event = new Event(hub, 'level')
    event.isTriggered = true
    // correct path put in folder
    var db = this.levelup = levelup('level-' + val, { db: leveldown || leveljs })
    db.open(function onOpen () {
      var r = db.createReadStream()
      r.on('data', function (data) {
        // write to correct hub
        var obj = setWithPath({}, data.key.split('.'), data.value)
        hub = hub.set(obj, event) || hub
        // hub.get(data.key, {}).set(data.value, event)
        console.log(data.key, '=', data.value)
      })
      .on('error', function (err) {
        hub.levelready.val = true
        console.log('Oh my!', err)
      })
      .on('close', function () {
        hub.levelready.val = true
        console.log('Stream closed')
      })
      .on('end', function () {
        event.trigger()
        hub.levelready.val = true
        console.log('Stream closed')
      })
      console.log('level times!'.blue)
    })
  }
}

// make a queue object
exports.on = {
  data: {
    level (data, event) {
      if (event.type === 'level') {
        return
      }
      var db = this.lookUp('levelup') // until you find it
      // level db path
      if (db && this.syncPath[0] !== 'clients') {
        if (typeof data !== 'object') {
          db.put(this.syncPath.join('.'), data, () => {
            console.log('wrote to level--->'.blue, data)
          })
        } else if (data === null) {
          db.del(this.syncPath.join('.'), function () {
            console.log('remove from level--->'.red, data)
          })
        }
        // if data null -- also remove all bested fields!
      }
      // console.log('level >'.grey, this.path.join('.'), db)
    }
  }
}
<|endoftext|>


<commit_before>/**
 * Module dependencies.
 */

var Transport = require('../transport')
  , parser = require('engine.io-parser')
  , util = require('../util')
  , debug = require('debug')('engine.io-client:websocket');

/**
 * Module exports.
 */

module.exports = WS;

/**
 * Global reference.
 */

var global = util.global();

/**
 * WebSocket transport constructor.
 *
 * @api {Object} connection options
 * @api public
 */

function WS(opts){
  Transport.call(this, opts);
};

/**
 * Inherits from Transport.
 */

util.inherits(WS, Transport);

/**
 * Transport name.
 *
 * @api public
 */

WS.prototype.name = 'websocket';

/**
 * Opens socket.
 *
 * @api private
 */

WS.prototype.doOpen = function(){
  if (!this.check()) {
    // let probe timeout
    return;
  }

  var self = this;

  this.socket = new (ws())(this.uri());
  this.socket.onopen = function(){
    self.onOpen();
  };
  this.socket.onclose = function(){
    self.onClose();
  };
  this.socket.onmessage = function(ev){
    self.onData(ev.data);
  };
  this.socket.onerror = function(e){
    self.onError('websocket error', e);
  };
};

/**
 * Writes data to socket.
 *
 * @param {Array} array of packets.
 * @api private
 */

WS.prototype.write = function(packets){
  var self = this;
  this.writable = false;
  // encodePacket efficient as it uses WS framing
  // no need for encodePayload
  for (var i = 0, l = packets.length; i < l; i++) {
    this.socket.send(parser.encodePacket(packets[i]));
  }
  function ondrain() {
    self.writable = true;
    self.emit('drain');
  }
  // check periodically if we're done sending
  if ('bufferedAmount' in this.socket) {
    this.bufferedAmountId = setInterval(function() {
      if (self.socket.bufferedAmount == 0) {
        clearInterval(self.bufferedAmountId);
        ondrain();
      }
    }, 50);
  } else {
    // fake drain
    // defer to next tick to allow Socket to clear writeBuffer
    setTimeout(ondrain, 0);
  }
};

/**
 * Called upon close
 *
 * @api private
 */

WS.prototype.onClose = function(){
  // stop checking to see if websocket is done sending buffer
  clearInterval(this.bufferedAmountId);
  Transport.prototype.onClose.call(this);
}

/**
 * Closes socket.
 *
 * @api private
 */

WS.prototype.doClose = function(){
  if (typeof this.socket !== 'undefined') {
    this.socket.close();
  }
};

/**
 * Generates uri for connection.
 *
 * @api private
 */

WS.prototype.uri = function(){
  var query = this.query || {};
  var schema = this.secure ? 'wss' : 'ws';
  var port = '';

  // avoid port if default for schema
  if (this.port && (('wss' == schema && this.port != 443)
    || ('ws' == schema && this.port != 80))) {
    port = ':' + this.port;
  }

  // append timestamp to URI
  if (this.timestampRequests) {
    query[this.timestampParam] = +new Date;
  }

  query = util.qs(query);

  // prepend ? to query
  if (query.length) {
    query = '?' + query;
  }

  return schema + '://' + this.hostname + port + this.path + query;
};

/**
 * Feature detection for WebSocket.
 *
 * @return {Boolean} whether this transport is available.
 * @api public
 */

WS.prototype.check = function(){
  var websocket = ws();
  return !!websocket && !('__initialize' in websocket && this.name === WS.prototype.name);
};

/**
 * Getter for WS constructor.
 *
 * @api private
 */

function ws(){
  if ('undefined' == typeof window) {
    return require('ws');
  }

  return global.WebSocket || global.MozWebSocket;
}
<commit_msg>transports: really fix iOS crash issue<commit_after>/**
 * Module dependencies.
 */

var Transport = require('../transport')
  , parser = require('engine.io-parser')
  , util = require('../util')
  , debug = require('debug')('engine.io-client:websocket');

/**
 * Module exports.
 */

module.exports = WS;

/**
 * Global reference.
 */

var global = util.global();

/**
 * WebSocket transport constructor.
 *
 * @api {Object} connection options
 * @api public
 */

function WS(opts){
  Transport.call(this, opts);
};

/**
 * Inherits from Transport.
 */

util.inherits(WS, Transport);

/**
 * Transport name.
 *
 * @api public
 */

WS.prototype.name = 'websocket';

/**
 * Opens socket.
 *
 * @api private
 */

WS.prototype.doOpen = function(){
  if (!this.check()) {
    // let probe timeout
    return;
  }

  var self = this;

  this.socket = new (ws())(this.uri());
  this.socket.onopen = function(){
    self.onOpen();
  };
  this.socket.onclose = function(){
    self.onClose();
  };
  this.socket.onmessage = function(ev){
    self.onData(ev.data);
  };
  this.socket.onerror = function(e){
    self.onError('websocket error', e);
  };
};

/**
 * Override `onData` to use a timer on iOS.
 * See: https://gist.github.com/mloughran/2052006
 *
 * @api private
 */

if ('undefined' != typeof navigator
  && /iPad|iPhone|iPod/i.test(navigator.userAgent)) {
  WS.prototype.onData = function(data){
    var self = this;
    setTimeout(function(){
      Transport.prototype.onData.call(self, data);
    }, 0);
  };
}

/**
 * Writes data to socket.
 *
 * @param {Array} array of packets.
 * @api private
 */

WS.prototype.write = function(packets){
  var self = this;
  this.writable = false;
  // encodePacket efficient as it uses WS framing
  // no need for encodePayload
  for (var i = 0, l = packets.length; i < l; i++) {
    this.socket.send(parser.encodePacket(packets[i]));
  }
  function ondrain() {
    self.writable = true;
    self.emit('drain');
  }
  // check periodically if we're done sending
  if ('bufferedAmount' in this.socket) {
    this.bufferedAmountId = setInterval(function() {
      if (self.socket.bufferedAmount == 0) {
        clearInterval(self.bufferedAmountId);
        ondrain();
      }
    }, 50);
  } else {
    // fake drain
    // defer to next tick to allow Socket to clear writeBuffer
    setTimeout(ondrain, 0);
  }
};

/**
 * Called upon close
 *
 * @api private
 */

WS.prototype.onClose = function(){
  // stop checking to see if websocket is done sending buffer
  clearInterval(this.bufferedAmountId);
  Transport.prototype.onClose.call(this);
};

/**
 * Closes socket.
 *
 * @api private
 */

WS.prototype.doClose = function(){
  if (typeof this.socket !== 'undefined') {
    this.socket.close();
  }
};

/**
 * Generates uri for connection.
 *
 * @api private
 */

WS.prototype.uri = function(){
  var query = this.query || {};
  var schema = this.secure ? 'wss' : 'ws';
  var port = '';

  // avoid port if default for schema
  if (this.port && (('wss' == schema && this.port != 443)
    || ('ws' == schema && this.port != 80))) {
    port = ':' + this.port;
  }

  // append timestamp to URI
  if (this.timestampRequests) {
    query[this.timestampParam] = +new Date;
  }

  query = util.qs(query);

  // prepend ? to query
  if (query.length) {
    query = '?' + query;
  }

  return schema + '://' + this.hostname + port + this.path + query;
};

/**
 * Feature detection for WebSocket.
 *
 * @return {Boolean} whether this transport is available.
 * @api public
 */

WS.prototype.check = function(){
  var websocket = ws();
  return !!websocket && !('__initialize' in websocket && this.name === WS.prototype.name);
};

/**
 * Getter for WS constructor.
 *
 * @api private
 */

function ws(){
  if ('undefined' == typeof window) {
    return require('ws');
  }

  return global.WebSocket || global.MozWebSocket;
}
<|endoftext|>


<commit_before>
/*!
 * socket.io-node
 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
 * MIT Licensed
 */

/**
 * Module requirements.
 */

var protocolVersions = require('./websocket/');

/**
 * Export the constructor.
 */

exports = module.exports = WebSocket;

/**
 * HTTP interface constructor. Interface compatible with all transports that
 * depend on request-response cycles.
 *
 * @api public
 */

function WebSocket (mng, data, req) {
  var transport
    , version = req.headers['sec-websocket-version'];
  if (typeof version !== 'undefined' && typeof protocolVersions[version] !== 'undefined') {
    transport = new protocolVersions[version](mng, data, req);
  }
  else transport = new protocolVersions['default'](mng, data, req);
  if (typeof this.name !== 'undefined') transport.name = this.name;
  return transport;
};
<commit_msg>transport: rewrite websocket transport to use ws client<commit_after>
/*!
 * socket.io-node
 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
 * MIT Licensed
 */

/**
 * Module requirements.
 */

var url = require('url')
  , parser = require('../parser')
  , Transport = require('../transport');

/**
 * Export the constructor.
 */

exports = module.exports = WebSocket;

/**
 * HTTP interface constructor. Interface compatible with all transports that
 * depend on request-response cycles.
 *
 * @api public
 */

function WebSocket (mng, data, req) {
  Transport.call(this, mng, data, req);
  var self = this;
  this.wsclient = req.wsclient;
  req.wsclient.onerror = function(){
    self.end('socket error');
  };
  req.wsclient.onclose = function(){
    self.end('socket end');
  };
  req.wsclient.onmessage = function(ev){
    self.onMessage(parser.decodePacket(ev.data));
  };
};

/**
 * Inherits from Transport.
 */

WebSocket.prototype.__proto__ = Transport.prototype;

/**
 * Transport name
 *
 * @api public
 */

WebSocket.prototype.name = 'websocket';

/**
 * Writes to the socket.
 *
 * @api private
 */

WebSocket.prototype.write = function (data) {
  this.wsclient.send(data);
};

/**
 * Closes the connection.
 *
 * @api private
 */

WebSocket.prototype.doClose = function () {
  this.req.socket.end();
};
<|endoftext|>


<commit_before>/*
 * Licensed to Cloudkick, Inc ('Cloudkick') under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Cloudkick licenses this file to You 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.
 */

/**
 * Configuration subsytem, providing a set of defaults, and merging of a configuration
 * JSON file.
 */

var fs = require('fs');
var path = require('path');

var async = require('extern/async');

var client_config = require('util/config');
var misc = require('util/misc');
var utilfs = require('util/fs');
var expanduser = require('util/misc').expanduser;
var fsutil = require('util/fs');

/**
 * The path to the dot cast directory (currently ~/.cast)
 */
var dot_cast = exports.dot_cast_path = misc.expanduser('~/.cast');

/**
 * The path to the remotes.json file in the dot cast directory
 */
var dot_cast_remotes_path = exports.dot_cast_remotes_path = path.join(dot_cast, 'remotes.json');

/**
 * The path to the remotes.json file for the current project
 */
var local_remotes_path = exports.local_remotes_path = function(projectpath) {
  projectpath = projectpath || process.cwd();
  return path.join(exports.dot_cast_project_path(projectpath), 'remotes.json');
};

/**
 * Get the path to the cast project directory for a project
 *
 * @param {String} projectpath  The path to the root of the project.
 */
exports.dot_cast_project_path = function(projectpath) {
  return path.join(projectpath, '.cast-project');
};

/**
 * Create the user's ~/.cast directory if it doesn't exist.
 *
 * @param {Function} callback A callback fired with a possible error.
 */
exports.ensure_dot_cast = function(callback) {
  fsutil.ensure_directory(dot_cast, callback);
};

/**
 * Creates a dot cast project directory if it doesn't exist.
 *
 * @param {String} projectpath The path to the project.
 * @param {Function} callback A callback fired upon completion with (err, dot_cast_project_path).
 */
exports.ensure_dot_cast_project = function(projectpath, callback) {
  var dot_cast_project_path = exports.dot_cast_project_path(projectpath);
  fsutil.ensure_directory(dot_cast_project_path, function(err) {
    return callback(err, dot_cast_project_path);
  });
};

/**
 * Attempt to load remotes from the specified file
 *
 * @param {String} p  The path to the file to load from
 * @param {Function} callback A callback taking (err, remotes)
 */
function load_remotes(p, callback) {
  path.exists(p, function(exists) {
    if (!exists) {
      callback(null, {});
      return;
    }
    fs.stat(p, function(err, stats) {
      if (err) {
        callback(err);
        return;
      }
      else if (!stats.isFile()) {
        callback(new Error(p + ' exists but is not a file'));
        return;
      }
      utilfs.json_file(p, callback);
    });
  });
}

/**
 * Load global remotes
 *
 * @param {Function} callback A callback taking (err, remotes).
 */
exports.get_global_remotes = function(callback) {
  load_remotes(dot_cast_remotes_path, callback);
};

/**
 * Load project remotes
 *
 * @param {Function} callback A callback taking (err, remotes).
 */
exports.get_local_remotes = function(callback) {
  load_remotes(local_remotes_path(), callback);
};

/**
 * Get the remotes object which will default to an empty object if the remotes
 * file doesn't exist. This will look in ~/.cast/remotes.json as well as in
 * .cast-project/remotes.json if they exist. If neither of these exists an
 * empty object will be returned.
 *
 * @param {Function} callback A callback fired with (err, remotes).
 */
exports.get_remotes = function(callback) {
  var global_remotes;
  var local_remotes;

  async.parallel([
    // Get the 'global' remotes
    function(callback) {
      exports.get_global_remotes(function(err, remotes) {
        if (!err) {
          global_remotes = remotes;
        }
        callback(err);
      });
    },

    // Get the 'local' remotes
    function(callback) {
      exports.get_local_remotes(function(err, remotes) {
        if (!err) {
          local_remotes = remotes;
        }
        callback(err);
      });
    }
  ],
  function(err) {
    if (err) {
      callback(err);
      return;
    }

    var remotes = global_remotes;
    var name;

    // Set the 'global' property to true on all global remotes
    for (name in remotes) {
      if (remotes.hasOwnProperty(name)) {
        remotes[name].global = true;
      }
    }

    // Set the 'global' property to false on all local remotes
    for (name in local_remotes) {
      if (local_remotes.hasOwnProperty(name)) {
        remotes[name] = local_remotes[name];
        remotes[name].global = false;
      }
    }

    // Push global remotes, setting them as global

    callback(null, remotes);
  });
};

/**
 * Get a remote by name, or the default if the name evaluates to false.
 *
 * @param {String} remote_name The name of the remote to retrieve
 * @param {Function} callback A callback called with (remote, err)
 */
exports.get_remote = function(remote_name, callback) {
  if (remote_name) {
    exports.get_remotes(function(err, remotes) {
      if (err) {
        callback(err);
      }
      else if (!remotes.hasOwnProperty(remote_name)) {
        callback(new Error("No such remote: " + remote_name));
      }
      else {
        callback(null, remotes[remote_name]);
      }
    });
  }
  else {
    exports.get_default_remote(callback);
  }
};

/*
 * Return a default remote.
 *
 * If a remotes file does not exist or there is a no default remote, callback will be called with null as
 * the second argument.
 *
 * @param {Function} callback Callback which is called with an error as the first argument and remote object
 *                            as the second one if a default remote is found.
 */
exports.get_default_remote = function(callback) {
  var global_default, local_default;
  exports.get_remotes(function(err, remotes) {
    if (err) {
      callback(err);
      return;
    }

    var remote, name;

    for (name in remotes) {
      if (remotes.hasOwnProperty(name)) {
        remote = remotes[name];

        if (remote.global && remote.is_default) {
          global_default = remote;
        }

        if (!remote.global && remote.is_default) {
          local_default = remote;
        }
      }
    }

    if (!local_default && !global_default) {
      callback(new Error('No default remote found'));
    }
    else {
      callback(null, local_default || global_default);
    }
  });
};

/**
 * Store a remotes (that is, a name -> obj mapping of remotes) to the specified
 * path.
 *
 * @param {String} p The path to save the remotes to
 * @param {Object} remotes The remotes object
 * @callback {Function} callback Callback fired with (err) upon completion
 */
exports.saveRemotes = function(p, remotes, callback) {
  var fstream = fs.createWriteStream(p);

  fstream.write(JSON.stringify(remotes, null, 4));
  fstream.end();

  fstream.on('close', function() {
    return callback();
  });

  fstream.on('error', function(err) {
    fstream.removeAllListeners('end');
    return callback(new Error('Error writing remotes file: ' + err));
  });
};

/**
 * Store the provided local remotes object.
 *
 * @param {Object} remotes The remotes object
 * @param {Function} callback Callback fired with (err)
 */
exports.saveLocalRemotes = function(remotes, callback) {
  exports.saveRemotes(local_remotes_path(), remotes, callback);
};

/**
 * Store the provided global remotes object.
 *
 * @param {Object} remotes The remotes object
 * @param {Function} callback Callback fired with (err)
 */
exports.saveGlobalRemotes = function(remotes, callback) {
  exports.saveRemotes(dot_cast_remotes_path, remotes, callback);
};

/**
 * Given the name of a bundle and the root of a project, get the path to the
 * bundle.
 *
 * @param {String} project_root Path to the project root
 * @param {String} bundle_name  The name of the bundle
 * @return {String} The path to the bundle file
 */
exports.get_bundle_path = function(project_root, bundle_name) {
  var dot_cast_project_path = exports.dot_cast_project_path(project_root);
  var bundle_file = bundle_name + '.tar.gz';
  return path.join(dot_cast_project_path, 'tmp', bundle_file);
};

/**
 * Get a list of (presumably bundle) files in the tmp directory
 *
 * @param {String} project_root Path to the project root
 * @param {Function} callback   A callback that takes (err, bundles)
 */
exports.list_bundles = function(project_root, callback) {
  var dot_cast_project_path = exports.dot_cast_project_path(project_root);
  var bundledir = path.join(dot_cast_project_path, 'tmp');

  fs.readdir(bundledir, function(err, files) {
    var msg, bundles;

    if (err) {
      msg = 'Unable to read ' + bundledir + ', have you created a bundle yet?';
      return callback(new Error(msg));
    }

    // Eliminate anything that doesn't look right
    files = files.filter(function(file) {
      return file.match(/.*@.*\.tar\.gz$/);
    });

    // Strip extensions
    bundles = files.map(function(file) {
      return path.basename(file, '.tar.gz');
    });

    return callback(null, bundles);
  });
};

/**
 * Get the name of the newest bundle in the tmp directory. Note that 'newest'
 * in this case is determined by mtime on the file, not the version string.
 *
 * @param {String} project_root The root of the project
 * @param {Function} callback   A callback that takes (err, bundle)
 */
exports.get_newest_bundle = function(project_root, callback) {
  exports.list_bundles(project_root, function(err, bundles) {
    var newestbundle;
    var newestmtime;
    var size;

    if (err) {
      return callback(err);
    }

    async.forEach(bundles, function(bundle, callback) {
      var bundlepath = exports.get_bundle_path(project_root, bundle);
      fs.stat(bundlepath, function(err, stats) {
        if (!err && stats.isFile() && (!newestmtime || stats.mtime > newestmtime)) {
          newestmtime = stats.mtime;
          newestbundle = bundle;
          size = stats.size;
        }
        return callback();
      });
    },
    function(err) {
      var msg;
      if (!err && !newestbundle) {
        msg = 'No bundles found, have you created one yet?';
        err = new Error(msg);
      }
      return callback(err, newestbundle);
    });
  });
};
<commit_msg>add dotfiles methods for getting client key/crt paths<commit_after>/*
 * Licensed to Cloudkick, Inc ('Cloudkick') under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Cloudkick licenses this file to You 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.
 */

/**
 * Configuration subsytem, providing a set of defaults, and merging of a configuration
 * JSON file.
 */

var fs = require('fs');
var path = require('path');

var async = require('extern/async');

var client_config = require('util/config');
var misc = require('util/misc');
var utilfs = require('util/fs');
var expanduser = require('util/misc').expanduser;
var fsutil = require('util/fs');

/**
 * Name of the client private key file
 * @type {String}
 */
var CLIENT_KEYFILE_NAME = 'cast.key';

/**
 * Name of the client x509 cert file
 * @type {String}
 */
var CLIENT_CRTFILE_NAME = 'cast.crt';

/**
 * Get the path to the client private key
 * @return {String} Path to the client private key
 */
exports.getClientKeyPath = function() {
  return path.join(exports.dot_cast_path, CLIENT_KEYFILE_NAME);
};

/**
 * Get the path to the client x509 certificate
 * @return {String} Path to the client certificate
 */
exports.getClientCertPath = function() {
  return path.join(exports.dot_cast_path, CLIENT_CRTFILE_NAME);
};

/**
 * The path to the dot cast directory (currently ~/.cast)
 */
var dot_cast = exports.dot_cast_path = misc.expanduser('~/.cast');

/**
 * The path to the remotes.json file in the dot cast directory
 */
var dot_cast_remotes_path = exports.dot_cast_remotes_path = path.join(dot_cast, 'remotes.json');

/**
 * The path to the remotes.json file for the current project
 */
var local_remotes_path = exports.local_remotes_path = function(projectpath) {
  projectpath = projectpath || process.cwd();
  return path.join(exports.dot_cast_project_path(projectpath), 'remotes.json');
};

/**
 * Get the path to the cast project directory for a project
 *
 * @param {String} projectpath  The path to the root of the project.
 */
exports.dot_cast_project_path = function(projectpath) {
  return path.join(projectpath, '.cast-project');
};

/**
 * Create the user's ~/.cast directory if it doesn't exist.
 *
 * @param {Function} callback A callback fired with a possible error.
 */
exports.ensure_dot_cast = function(callback) {
  fsutil.ensure_directory(dot_cast, callback);
};

/**
 * Creates a dot cast project directory if it doesn't exist.
 *
 * @param {String} projectpath The path to the project.
 * @param {Function} callback A callback fired upon completion with (err, dot_cast_project_path).
 */
exports.ensure_dot_cast_project = function(projectpath, callback) {
  var dot_cast_project_path = exports.dot_cast_project_path(projectpath);
  fsutil.ensure_directory(dot_cast_project_path, function(err) {
    return callback(err, dot_cast_project_path);
  });
};

/**
 * Attempt to load remotes from the specified file
 *
 * @param {String} p  The path to the file to load from
 * @param {Function} callback A callback taking (err, remotes)
 */
function load_remotes(p, callback) {
  path.exists(p, function(exists) {
    if (!exists) {
      callback(null, {});
      return;
    }
    fs.stat(p, function(err, stats) {
      if (err) {
        callback(err);
        return;
      }
      else if (!stats.isFile()) {
        callback(new Error(p + ' exists but is not a file'));
        return;
      }
      utilfs.json_file(p, callback);
    });
  });
}

/**
 * Load global remotes
 *
 * @param {Function} callback A callback taking (err, remotes).
 */
exports.get_global_remotes = function(callback) {
  load_remotes(dot_cast_remotes_path, callback);
};

/**
 * Load project remotes
 *
 * @param {Function} callback A callback taking (err, remotes).
 */
exports.get_local_remotes = function(callback) {
  load_remotes(local_remotes_path(), callback);
};

/**
 * Get the remotes object which will default to an empty object if the remotes
 * file doesn't exist. This will look in ~/.cast/remotes.json as well as in
 * .cast-project/remotes.json if they exist. If neither of these exists an
 * empty object will be returned.
 *
 * @param {Function} callback A callback fired with (err, remotes).
 */
exports.get_remotes = function(callback) {
  var global_remotes;
  var local_remotes;

  async.parallel([
    // Get the 'global' remotes
    function(callback) {
      exports.get_global_remotes(function(err, remotes) {
        if (!err) {
          global_remotes = remotes;
        }
        callback(err);
      });
    },

    // Get the 'local' remotes
    function(callback) {
      exports.get_local_remotes(function(err, remotes) {
        if (!err) {
          local_remotes = remotes;
        }
        callback(err);
      });
    }
  ],
  function(err) {
    if (err) {
      callback(err);
      return;
    }

    var remotes = global_remotes;
    var name;

    // Set the 'global' property to true on all global remotes
    for (name in remotes) {
      if (remotes.hasOwnProperty(name)) {
        remotes[name].global = true;
      }
    }

    // Set the 'global' property to false on all local remotes
    for (name in local_remotes) {
      if (local_remotes.hasOwnProperty(name)) {
        remotes[name] = local_remotes[name];
        remotes[name].global = false;
      }
    }

    // Push global remotes, setting them as global

    callback(null, remotes);
  });
};

/**
 * Get a remote by name, or the default if the name evaluates to false.
 *
 * @param {String} remote_name The name of the remote to retrieve
 * @param {Function} callback A callback called with (remote, err)
 */
exports.get_remote = function(remote_name, callback) {
  if (remote_name) {
    exports.get_remotes(function(err, remotes) {
      if (err) {
        callback(err);
      }
      else if (!remotes.hasOwnProperty(remote_name)) {
        callback(new Error("No such remote: " + remote_name));
      }
      else {
        callback(null, remotes[remote_name]);
      }
    });
  }
  else {
    exports.get_default_remote(callback);
  }
};

/*
 * Return a default remote.
 *
 * If a remotes file does not exist or there is a no default remote, callback will be called with null as
 * the second argument.
 *
 * @param {Function} callback Callback which is called with an error as the first argument and remote object
 *                            as the second one if a default remote is found.
 */
exports.get_default_remote = function(callback) {
  var global_default, local_default;
  exports.get_remotes(function(err, remotes) {
    if (err) {
      callback(err);
      return;
    }

    var remote, name;

    for (name in remotes) {
      if (remotes.hasOwnProperty(name)) {
        remote = remotes[name];

        if (remote.global && remote.is_default) {
          global_default = remote;
        }

        if (!remote.global && remote.is_default) {
          local_default = remote;
        }
      }
    }

    if (!local_default && !global_default) {
      callback(new Error('No default remote found'));
    }
    else {
      callback(null, local_default || global_default);
    }
  });
};

/**
 * Store a remotes (that is, a name -> obj mapping of remotes) to the specified
 * path.
 *
 * @param {String} p The path to save the remotes to
 * @param {Object} remotes The remotes object
 * @callback {Function} callback Callback fired with (err) upon completion
 */
exports.saveRemotes = function(p, remotes, callback) {
  var fstream = fs.createWriteStream(p);

  fstream.write(JSON.stringify(remotes, null, 4));
  fstream.end();

  fstream.on('close', function() {
    return callback();
  });

  fstream.on('error', function(err) {
    fstream.removeAllListeners('end');
    return callback(new Error('Error writing remotes file: ' + err));
  });
};

/**
 * Store the provided local remotes object.
 *
 * @param {Object} remotes The remotes object
 * @param {Function} callback Callback fired with (err)
 */
exports.saveLocalRemotes = function(remotes, callback) {
  exports.saveRemotes(local_remotes_path(), remotes, callback);
};

/**
 * Store the provided global remotes object.
 *
 * @param {Object} remotes The remotes object
 * @param {Function} callback Callback fired with (err)
 */
exports.saveGlobalRemotes = function(remotes, callback) {
  exports.saveRemotes(dot_cast_remotes_path, remotes, callback);
};

/**
 * Given the name of a bundle and the root of a project, get the path to the
 * bundle.
 *
 * @param {String} project_root Path to the project root
 * @param {String} bundle_name  The name of the bundle
 * @return {String} The path to the bundle file
 */
exports.get_bundle_path = function(project_root, bundle_name) {
  var dot_cast_project_path = exports.dot_cast_project_path(project_root);
  var bundle_file = bundle_name + '.tar.gz';
  return path.join(dot_cast_project_path, 'tmp', bundle_file);
};

/**
 * Get a list of (presumably bundle) files in the tmp directory
 *
 * @param {String} project_root Path to the project root
 * @param {Function} callback   A callback that takes (err, bundles)
 */
exports.list_bundles = function(project_root, callback) {
  var dot_cast_project_path = exports.dot_cast_project_path(project_root);
  var bundledir = path.join(dot_cast_project_path, 'tmp');

  fs.readdir(bundledir, function(err, files) {
    var msg, bundles;

    if (err) {
      msg = 'Unable to read ' + bundledir + ', have you created a bundle yet?';
      return callback(new Error(msg));
    }

    // Eliminate anything that doesn't look right
    files = files.filter(function(file) {
      return file.match(/.*@.*\.tar\.gz$/);
    });

    // Strip extensions
    bundles = files.map(function(file) {
      return path.basename(file, '.tar.gz');
    });

    return callback(null, bundles);
  });
};

/**
 * Get the name of the newest bundle in the tmp directory. Note that 'newest'
 * in this case is determined by mtime on the file, not the version string.
 *
 * @param {String} project_root The root of the project
 * @param {Function} callback   A callback that takes (err, bundle)
 */
exports.get_newest_bundle = function(project_root, callback) {
  exports.list_bundles(project_root, function(err, bundles) {
    var newestbundle;
    var newestmtime;
    var size;

    if (err) {
      return callback(err);
    }

    async.forEach(bundles, function(bundle, callback) {
      var bundlepath = exports.get_bundle_path(project_root, bundle);
      fs.stat(bundlepath, function(err, stats) {
        if (!err && stats.isFile() && (!newestmtime || stats.mtime > newestmtime)) {
          newestmtime = stats.mtime;
          newestbundle = bundle;
          size = stats.size;
        }
        return callback();
      });
    },
    function(err) {
      var msg;
      if (!err && !newestbundle) {
        msg = 'No bundles found, have you created one yet?';
        err = new Error(msg);
      }
      return callback(err, newestbundle);
    });
  });
};
<|endoftext|>


<commit_before>// ==UserScript==
// @name        SubScene Filter
// @namespace   www.subscene.com
// @description Add filter to subscene searches
// @include     https://*subscene.com/subtitles/*
// @exclude     https://*subscene.com/subtitles/title?q*
// @exclude     https://*subscene.com/subtitles/*/*
// @require  http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
// @version     1
// @grant       none
// ==/UserScript==
$("#flagWrapper").after('
<input type="text"><button id="mytoggle" type="button">Toggle</button>
');
var rows = $("tr").filter(function(ix,el) {
  return $(this).find("span.l").text();
});
$("#mytoggle").click(function() {
  rows.filter(function(ix,el) {
    return $(this).find("span.l").text().trim() != "English";
  })
    .hide();
});
<commit_msg>Add basic filter<commit_after>// ==UserScript==
// @name        SubScene Filter
// @namespace   www.subscene.com
// @description Add filter to subscene searches
// @include     https://*subscene.com/subtitles/*
// @exclude     https://*subscene.com/subtitles/title?q*
// @exclude     https://*subscene.com/subtitles/*/*
// @require  http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
// @version     1
// @grant       none
// ==/UserScript==
$("#flagWrapper").after('
<input id="myfilterquery" type="text"><button id="mytoggle" type="button">Toggle</button>
');
var rows = $("tr").filter(function(ix,el) {
  return $(this).find("span.l").text();
});
$("#mytoggle").click(function() {
  rows.filter(function(ix,el) {
    return $(this).find("span.l").text().trim() != "English";
  })
    .hide();
});
$("#myfilterquery").submit(function() {
  var isValid = true;
  try {
    var inputRegex = new RegExp($(this).val());
  } catch(e) {
    isValid = false;
  }

  if(!isValid) {
    window.console.log("Regex not valid");
    return $(this);
  }

  rows.filter(function(ix,el) {
    return ! $(this).children("td.a1 a span:nth-child(2)").text().trim().match(inputRegex);
  })
    .hide();
});
<|endoftext|>


<commit_before>/*!
 * Subtitle.js
 * Parse and manipulate SRT (SubRip)
 * https://github.com/gsantiago/subtitle.js
 *
 * @version 0.0.2
 * @author Guilherme Santiago
*/

/**
 * @constructor
 * @param {String} Optional SRT content to be parsed
*/
function Subtitle (srt) {
  this.subtitles = [];

  if (srt) {
    this.parse(srt);
  }
}

/**
 * SRT parser
*/
Subtitle.prototype.parse = function (srt) {
  var subs = [];
  var index;
  var time;
  var text;
  var start;
  var end;

  if (!srt) {
    throw new Error('No SRT to parse');
  }

  srt = srt.split('\n');

  srt.forEach(function (line) {
    line = line.toString();

    // if we don't have an index, so we should expect an index
    if (!index) {
      if (/^\d+$/.test(line)) {
        index = parseInt(line);
        return;
      }
    }

    // now we have to check for the time
    if (!time) {
      var match = line.match(/^(\d{2}:\d{2}:\d{2},\d{3}) --> (\d{2}:\d{2}:\d{2},\d{3})$/);
      if (match) {
        start = match[1];
        end = match[2];
        time = true;
        return;
      }
    }

    // now we get all the strings until we get an empty line
    if (line.trim() === '') {
      subs.push({
        index: index,
        start: start,
        end: end,
        text: text
      });
      index = time = start = end = text = null;
    } else {
      if (!text) {
        text = line;
      } else {
        text += '\n' + line;
      }
    }

  });

  this.subtitles = subs;

  return this;
};

/**
 * Add a caption
 * You have to pass an object containing the following data:
 * start - The start time
 * end - The end time
 * text - The caption text
 *
 * The start and end time support two patterns:
 * The SRT: '00:00:24,400'
 * Or a positive integer representing milliseconds
 *
 * @public
 * @param {Object} Caption data
*/
Subtitle.prototype.add = function (caption) {
  if (!caption.start || !caption.end || !caption.text) {
    throw new Error('Invalid caption data');
  }

  var start = caption.start;
  var end = caption.end;
  var text = caption.text;

  if (/^\d+$/.test(start)) {
    start = Subtitle.toSrtTime(start);
  } else if (/^(\d{2}):(\d{2}):(\d{2}),(\d{3})$/.test(start)) {
    start = start;
  } else {
    throw new Error('Invalid caption time format');
  }

  if (/^\d+$/.test(end)) {
    end = Subtitle.toSrtTime(end);
  } else if (/^(\d{2}):(\d{2}):(\d{2}),(\d{3})$/.test(end)) {
    end = end;
  } else {
    throw new Error('Invalid caption time format');
  }

  this.subtitles.push({
    index: this.subtitles.length + 1,
    start: start,
    end: end,
    text: text
  });

  return this;
};

/**
 * Convert the SRT time format to milliseconds
 *
 * @static
 * @param {String} SRT time format
*/
Subtitle.toMS = function (time) {
  var match = time.match(/^(\d{2}):(\d{2}):(\d{2}),(\d{3})$/);

  if (!match) {
    throw new Error('Invalid SRT time format');
  }

  var hours = parseInt(match[1], 10);
  var minutes = parseInt(match[2], 10);
  var seconds = parseInt(match[3], 10);
  var milliseconds = parseInt(match[4], 10);

  hours *= 3600000;
  minutes *= 60000;
  seconds *= 1000;

  return hours + minutes + seconds + milliseconds;
};

/**
 * Convert milliseconds to SRT time format
 *
 * @static
 * @param {Integer} Milleseconds
*/
Subtitle.toSrtTime = function (time) {
  if (!/^\d+$/.test(time.toString())) {
    throw new Error('Time should be an Integer value in milliseconds');
  }

  time = parseInt(time);

  var date = new Date(0, 0, 0, 0, 0, 0, time);

  var hours = date.getHours() < 10
    ? '0' + date.getHours()
    : date.getHours();

  var minutes = date.getMinutes() < 10
    ? '0' + date.getMinutes()
    : date.getMinutes();

  var seconds = date.getSeconds() < 10
    ? '0' + date.getSeconds()
    : date.getSeconds();

  var ms = time - ((hours * 3600000) + (minutes * 60000) + (seconds * 1000));

  if (ms < 100 && ms > 10) {
    ms = '0' + ms;
  } else if (ms < 10) {
    ms = '00' + ms;
  }

  var srtTime = hours + ':' + minutes + ':' + seconds + ',' + ms;

  return srtTime;
};

module.exports = Subtitle;
<commit_msg>Add TODO comment<commit_after>/*!
 * Subtitle.js
 * Parse and manipulate SRT (SubRip)
 * https://github.com/gsantiago/subtitle.js
 *
 * @version 0.0.2
 * @author Guilherme Santiago
*/

/**
 * @constructor
 * @param {String} Optional SRT content to be parsed
*/
function Subtitle (srt) {
  this.subtitles = [];

  if (srt) {
    this.parse(srt);
  }
}

/**
 * SRT parser
*/
Subtitle.prototype.parse = function (srt) {
  var subs = [];
  var index;
  var time;
  var text;
  var start;
  var end;

  if (!srt) {
    throw new Error('No SRT to parse');
  }

  srt = srt.split('\n');

  srt.forEach(function (line) {
    line = line.toString();

    // if we don't have an index, so we should expect an index
    if (!index) {
      if (/^\d+$/.test(line)) {
        index = parseInt(line);
        return;
      }
    }

    // now we have to check for the time
    if (!time) {
      var match = line.match(/^(\d{2}:\d{2}:\d{2},\d{3}) --> (\d{2}:\d{2}:\d{2},\d{3})$/);
      if (match) {
        start = match[1];
        end = match[2];
        time = true;
        return;
      }
    }

    // now we get all the strings until we get an empty line
    if (line.trim() === '') {
      subs.push({
        index: index,
        start: start,
        end: end,
        text: text
      });
      index = time = start = end = text = null;
    } else {
      if (!text) {
        text = line;
      } else {
        text += '\n' + line;
      }
    }

  });

  this.subtitles = subs;

  return this;
};

/**
 * Add a caption
 * You have to pass an object containing the following data:
 * start - The start time
 * end - The end time
 * text - The caption text
 *
 * The start and end time support two patterns:
 * The SRT: '00:00:24,400'
 * Or a positive integer representing milliseconds
 *
 * @public
 * @param {Object} Caption data
*/
Subtitle.prototype.add = function (caption) {
  if (!caption.start || !caption.end || !caption.text) {
    throw new Error('Invalid caption data');
  }

  var start = caption.start;
  var end = caption.end;
  var text = caption.text;

  // TODO:
  // Refactoring the code below.
  // There are too much repetition
  if (/^\d+$/.test(start)) {
    start = Subtitle.toSrtTime(start);
  } else if (/^(\d{2}):(\d{2}):(\d{2}),(\d{3})$/.test(start)) {
    start = start;
  } else {
    throw new Error('Invalid caption time format');
  }

  if (/^\d+$/.test(end)) {
    end = Subtitle.toSrtTime(end);
  } else if (/^(\d{2}):(\d{2}):(\d{2}),(\d{3})$/.test(end)) {
    end = end;
  } else {
    throw new Error('Invalid caption time format');
  }

  this.subtitles.push({
    index: this.subtitles.length + 1,
    start: start,
    end: end,
    text: text
  });

  return this;
};

/**
 * Convert the SRT time format to milliseconds
 *
 * @static
 * @param {String} SRT time format
*/
Subtitle.toMS = function (time) {
  var match = time.match(/^(\d{2}):(\d{2}):(\d{2}),(\d{3})$/);

  if (!match) {
    throw new Error('Invalid SRT time format');
  }

  var hours = parseInt(match[1], 10);
  var minutes = parseInt(match[2], 10);
  var seconds = parseInt(match[3], 10);
  var milliseconds = parseInt(match[4], 10);

  hours *= 3600000;
  minutes *= 60000;
  seconds *= 1000;

  return hours + minutes + seconds + milliseconds;
};

/**
 * Convert milliseconds to SRT time format
 *
 * @static
 * @param {Integer} Milleseconds
*/
Subtitle.toSrtTime = function (time) {
  if (!/^\d+$/.test(time.toString())) {
    throw new Error('Time should be an Integer value in milliseconds');
  }

  time = parseInt(time);

  var date = new Date(0, 0, 0, 0, 0, 0, time);

  var hours = date.getHours() < 10
    ? '0' + date.getHours()
    : date.getHours();

  var minutes = date.getMinutes() < 10
    ? '0' + date.getMinutes()
    : date.getMinutes();

  var seconds = date.getSeconds() < 10
    ? '0' + date.getSeconds()
    : date.getSeconds();

  var ms = time - ((hours * 3600000) + (minutes * 60000) + (seconds * 1000));

  if (ms < 100 && ms > 10) {
    ms = '0' + ms;
  } else if (ms < 10) {
    ms = '00' + ms;
  }

  var srtTime = hours + ':' + minutes + ':' + seconds + ',' + ms;

  return srtTime;
};

module.exports = Subtitle;
<|endoftext|>