<commit_before>// activity.js
//
// data object representing an activity
//
// Copyright 2011,2012 StatusNet Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

var databank = require('databank'),
    Step = require('step'),
    _ = require('underscore'),
    URLMaker = require('../urlmaker').URLMaker,
    IDMaker = require('../idmaker').IDMaker,
    Stamper = require('../stamper').Stamper,
    ActivityObject = require('./activityobject').ActivityObject,
    DatabankObject = databank.DatabankObject;

var Activity = DatabankObject.subClass('activity');

Activity.schema = { pkey: 'id',
                    fields: ['actor',
                             'content',
                             'generator',
                             'icon',
                             'id',
                             'object',
                             'published',
                             'provider',
                             'target',
                             'title',
                             'url',
                             'uuid',
                             'updated',
                             'verb'],
                    indices: ['actor.id', 'object.id', 'uuid'] };

Activity.verbs = ['add',
                  'cancel',
                  'checkin',
                  'delete',
                  'favorite',
                  'follow',
                  'give',
                  'ignore',
                  'invite',
                  'join',
                  'leave',
                  'like',
                  'make-friend',
                  'play',
                  'post',
                  'receive',
                  'remove',
                  'remove-friend',
                  'request-friend',
                  'rsvp-maybe',
                  'rsvp-no',
                  'rsvp-yes',
                  'save',
                  'share',
                  'stop-following',
                  'tag',
                  'unfavorite',
                  'unlike',
                  'unsave',
                  'update'];

var i = 0, verb;

// Constants-like members for activity verbs

for (i = 0; i < Activity.verbs.length; i++) {
    verb = Activity.verbs[i];
    Activity[verb.toUpperCase().replace('-', '_')] = verb;
}

Activity.init = function(inst, properties) {

    DatabankObject.init(inst, properties);

    if (!this.verb) {
        this.verb = "post";
    }

    if (inst.actor) {
        inst.actor = ActivityObject.toObject(inst.actor, ActivityObject.PERSON);
    }

    if (inst.object) {
        inst.object = ActivityObject.toObject(inst.object);
    }
};

Activity.prototype.apply = function(defaultActor, callback) {

    var Edge = require('./edge').Edge;

    // Ensure an actor

    this.actor = this.actor || defaultActor;

    // XXX: Polymorphism is probably the right thing here
    // but I kinda CBA. How's this: rewrite when we get over 5 case's...?

    switch (this.verb) {
    case Activity.POST:
        // Force author data
        this.object.author = this.actor;
        // Is this it...?
        ActivityObject.createObject(this.object, function(err, result) {
            callback(err, result);
        });
        break;
    case Activity.FOLLOW:
        if (!this.actor.id || !this.object.id) {
            callback(new Error("No ID."));
        }
        // XXX: OStatus if necessary
        Edge.create({id: this.actor.id + '->' + this.object.id,
                     from: { id: this.actor.id, objectType: this.actor.objectType },
                     to: {id: this.object.id, objectType: this.object.objectType }},
                    callback);
        break;
    case Activity.STOP_FOLLOWING:
        var edges = [];
        // XXX: OStatus if necessary
        Edge.search({'from.id': this.actor.id, 'to.id': this.object.id}, function(err, edges) {
            if (err) {
                callback(err);
            } else if (edges.length === 0) { // that's bad
                callback(new Error("No such edge."));
            } else if (edges.length > 1) { // that's worse
                // XXX: Kill 'em all
                callback(new Error("Too many edges."));
            } else {
                edges[0].del(callback);
            }
        });
        break;
    default:
        // XXX: fave/unfave, join/leave, ...?
        callback(null);
        break;
    }
};

// XXX: identical to save

Activity.beforeCreate = function(props, callback) {

    var now = Stamper.stamp();

    props.updated = now;

    if (!props.published) {
        props.published = now;
    }

    if (!props.id) {
        props.uuid = IDMaker.makeID();
        props.id   = ActivityObject.makeURI('activity', props.uuid);
        props.links = {
            self: URLMaker.makeURL('api/activity/' + props.uuid)
        };
        // FIXME: assumes person data was set and that it's a local actor
        props.url  = URLMaker.makeURL(props.actor.preferredUsername + '/activity/' + props.uuid);

        // default verb

        if (!props.verb) {
            props.verb = 'post';
        }
    }

    if (!props.actor) {
        callback(new Error("Activity has no actor"), null);
    }

    if (!props.object) {
        callback(new Error("Activity has no object"), null);
    }

    Step(
        function() {
            ActivityObject.compressProperty(props, 'actor', this.parallel());
            ActivityObject.compressProperty(props, 'object', this.parallel());
        },
        function(err) {
            if (err) {
                callback(err, null);
            } else {
                callback(null, props);
            }
        }
    );
};

Activity.prototype.beforeUpdate = function(props, callback) {

    var now = Stamper.stamp();

    props.updated = now;

    Step(
        function() {
            ActivityObject.compressProperty(props, 'actor', this.parallel());
            ActivityObject.compressProperty(props, 'object', this.parallel());
        },
        function(err) {
            if (err) {
                callback(err, null);
            } else {
                callback(null, props);
            }
        }
    );
};

// When save()'ing an activity, ensure the actor and object
// are persisted, then save them by reference.

Activity.prototype.beforeSave = function(callback) {

    var now = Stamper.stamp(),
        act = this;

    act.updated = now;

    if (!act.published) {
        act.published = now;
    }

    if (!act.id) {
        act.uuid = IDMaker.makeID();
        act.id   = ActivityObject.makeURI('activity', act.uuid);
        act.links = {};
        act.links.self = URLMaker.makeURL('api/activity/' + act.uuid);
        // FIXME: assumes person data was set and that it's a local actor
        act.url  = URLMaker.makeURL(act.actor.preferredUsername + '/activity/' + act.uuid);
    }

    if (!act.actor) {
        callback(new Error("Activity has no actor"));
        return;
    }

    if (!act.object) {
        callback(new Error("Activity has no object"));
        return;
    }

    Step(
        function() {
            ActivityObject.compressProperty(act, 'actor', this.parallel());
            ActivityObject.compressProperty(act, 'object', this.parallel());
        },
        function(err) {
            if (err) {
                callback(err);
            } else {
                callback(null);
            }
        }
    );
};

// When get()'ing an activity, also get the actor and the object,
// which are saved by reference

Activity.prototype.afterCreate =
Activity.prototype.afterSave =
Activity.prototype.afterUpdate =
Activity.prototype.afterGet = function(callback) {
    this.expand(callback);
};

Activity.prototype.expand = function(callback) {
    var act = this;

    Step(
        function() {
            ActivityObject.expandProperty(act, 'actor', this.parallel());
            ActivityObject.expandProperty(act, 'object', this.parallel());
        },
        function(err) {
            if (err) {
                callback(err);
            } else {
                // Implied
                if (act.verb === 'post' && _(act.object).has('author')) {
                    delete act.object.author;
                }
                callback(null);
            }
        }
    );
};

Activity.prototype.compress = function(callback) {
    var act = this;
    Step(
        function() {
            ActivityObject.compressProperty(act, 'actor', this.parallel());
            ActivityObject.compressProperty(act, 'object', this.parallel());
        },
        function(err) {
            if (err) {
                callback(err);
            } else {
                callback(null);
            }
        }
    );
};

exports.Activity = Activity;
<commit_msg>Use follow() and stopFollowing() to apply those verbs<commit_after>// activity.js
//
// data object representing an activity
//
// Copyright 2011,2012 StatusNet Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

var databank = require('databank'),
    Step = require('step'),
    _ = require('underscore'),
    URLMaker = require('../urlmaker').URLMaker,
    IDMaker = require('../idmaker').IDMaker,
    Stamper = require('../stamper').Stamper,
    ActivityObject = require('./activityobject').ActivityObject,
    DatabankObject = databank.DatabankObject;

var Activity = DatabankObject.subClass('activity');

Activity.schema = { pkey: 'id',
                    fields: ['actor',
                             'content',
                             'generator',
                             'icon',
                             'id',
                             'object',
                             'published',
                             'provider',
                             'target',
                             'title',
                             'url',
                             'uuid',
                             'updated',
                             'verb'],
                    indices: ['actor.id', 'object.id', 'uuid'] };

Activity.verbs = ['add',
                  'cancel',
                  'checkin',
                  'delete',
                  'favorite',
                  'follow',
                  'give',
                  'ignore',
                  'invite',
                  'join',
                  'leave',
                  'like',
                  'make-friend',
                  'play',
                  'post',
                  'receive',
                  'remove',
                  'remove-friend',
                  'request-friend',
                  'rsvp-maybe',
                  'rsvp-no',
                  'rsvp-yes',
                  'save',
                  'share',
                  'stop-following',
                  'tag',
                  'unfavorite',
                  'unlike',
                  'unsave',
                  'update'];

var i = 0, verb;

// Constants-like members for activity verbs

for (i = 0; i < Activity.verbs.length; i++) {
    verb = Activity.verbs[i];
    Activity[verb.toUpperCase().replace('-', '_')] = verb;
}

Activity.init = function(inst, properties) {

    DatabankObject.init(inst, properties);

    if (!this.verb) {
        this.verb = "post";
    }

    if (inst.actor) {
        inst.actor = ActivityObject.toObject(inst.actor, ActivityObject.PERSON);
    }

    if (inst.object) {
        inst.object = ActivityObject.toObject(inst.object);
    }
};

Activity.prototype.apply = function(defaultActor, callback) {

    var act = this;

    // Ensure an actor

    this.actor = this.actor || defaultActor;

    // XXX: Polymorphism is probably the right thing here
    // but I kinda CBA. How's this: rewrite when we get over 5 case's...?

    switch (this.verb) {
    case Activity.POST:
        // Force author data
        this.object.author = this.actor;
        // Is this it...?
        ActivityObject.createObject(this.object, function(err, result) {
            callback(err, result);
        });
        break;
    case Activity.FOLLOW:
        if (!this.actor.id || !this.object.id) {
            callback(new Error("No ID."));
        }
        var User = require('./user').User;

        ActivityObject.ensureObject(act.object, function(err, followed) {
            User.fromPerson(act.actor.id, function(err, user) {
                if (err) {
                    callback(err, null);
                } else {
                    user.follow(act.actor.id, callback);
                }
            });
        });
        break;
    case Activity.STOP_FOLLOWING:
        // XXX: OStatus if necessary
        ActivityObject.ensureObject(act.object, function(err, followed) {
            User.fromPerson(act.actor.id, function(err, user) {
                if (err) {
                    callback(err, null);
                } else {
                    user.stopFollowing(act.actor.id, callback);
                }
            });
        });
        break;
    default:
        // XXX: fave/unfave, join/leave, ...?
        callback(null);
        break;
    }
};

// XXX: identical to save

Activity.beforeCreate = function(props, callback) {

    var now = Stamper.stamp();

    props.updated = now;

    if (!props.published) {
        props.published = now;
    }

    if (!props.id) {
        props.uuid = IDMaker.makeID();
        props.id   = ActivityObject.makeURI('activity', props.uuid);
        props.links = {
            self: URLMaker.makeURL('api/activity/' + props.uuid)
        };
        // FIXME: assumes person data was set and that it's a local actor
        props.url  = URLMaker.makeURL(props.actor.preferredUsername + '/activity/' + props.uuid);

        // default verb

        if (!props.verb) {
            props.verb = 'post';
        }
    }

    if (!props.actor) {
        callback(new Error("Activity has no actor"), null);
    }

    if (!props.object) {
        callback(new Error("Activity has no object"), null);
    }

    Step(
        function() {
            ActivityObject.compressProperty(props, 'actor', this.parallel());
            ActivityObject.compressProperty(props, 'object', this.parallel());
        },
        function(err) {
            if (err) {
                callback(err, null);
            } else {
                callback(null, props);
            }
        }
    );
};

Activity.prototype.beforeUpdate = function(props, callback) {

    var now = Stamper.stamp();

    props.updated = now;

    Step(
        function() {
            ActivityObject.compressProperty(props, 'actor', this.parallel());
            ActivityObject.compressProperty(props, 'object', this.parallel());
        },
        function(err) {
            if (err) {
                callback(err, null);
            } else {
                callback(null, props);
            }
        }
    );
};

// When save()'ing an activity, ensure the actor and object
// are persisted, then save them by reference.

Activity.prototype.beforeSave = function(callback) {

    var now = Stamper.stamp(),
        act = this;

    act.updated = now;

    if (!act.published) {
        act.published = now;
    }

    if (!act.id) {
        act.uuid = IDMaker.makeID();
        act.id   = ActivityObject.makeURI('activity', act.uuid);
        act.links = {};
        act.links.self = URLMaker.makeURL('api/activity/' + act.uuid);
        // FIXME: assumes person data was set and that it's a local actor
        act.url  = URLMaker.makeURL(act.actor.preferredUsername + '/activity/' + act.uuid);
    }

    if (!act.actor) {
        callback(new Error("Activity has no actor"));
        return;
    }

    if (!act.object) {
        callback(new Error("Activity has no object"));
        return;
    }

    Step(
        function() {
            ActivityObject.compressProperty(act, 'actor', this.parallel());
            ActivityObject.compressProperty(act, 'object', this.parallel());
        },
        function(err) {
            if (err) {
                callback(err);
            } else {
                callback(null);
            }
        }
    );
};

// When get()'ing an activity, also get the actor and the object,
// which are saved by reference

Activity.prototype.afterCreate =
Activity.prototype.afterSave =
Activity.prototype.afterUpdate =
Activity.prototype.afterGet = function(callback) {
    this.expand(callback);
};

Activity.prototype.expand = function(callback) {
    var act = this;

    Step(
        function() {
            ActivityObject.expandProperty(act, 'actor', this.parallel());
            ActivityObject.expandProperty(act, 'object', this.parallel());
        },
        function(err) {
            if (err) {
                callback(err);
            } else {
                // Implied
                if (act.verb === 'post' && _(act.object).has('author')) {
                    delete act.object.author;
                }
                callback(null);
            }
        }
    );
};

Activity.prototype.compress = function(callback) {
    var act = this;
    Step(
        function() {
            ActivityObject.compressProperty(act, 'actor', this.parallel());
            ActivityObject.compressProperty(act, 'object', this.parallel());
        },
        function(err) {
            if (err) {
                callback(err);
            } else {
                callback(null);
            }
        }
    );
};

exports.Activity = Activity;
<|endoftext|>


<commit_before>'use strict';

class TogglePanel {
  setEnabled(enabled = true) {
    this.enabled = enabled;
  }

  init() {
    if (this.enabled) {
      $('#compare').addClass('togglePanelWrapper');
      $('.diff-container .heading .primary').click(function() {
        $(this).parents('.bb-udiff').toggleClass('collapsed');
      });
    }
  }

  getButton() {
    const toggleAllPanels = this.toggleAllPanels;
    if (!this.enabled) {
      return '';
    } else {
      const btn = $('<button id="togglePanels" class="bb_menu_item aui-button" />')
        .text('Panels')
        .prepend('')
        .click(toggleAllPanels);
      return btn;
    }
  }

  toggleAllPanels() {
    // TODO: Review the correct behavior of the toggle button, now the first defines.
    const collapse = !$('.bb-udiff:first').hasClass('collapsed');
    if (collapse) {
      $('.bb-udiff').addClass('collapsed');
    } else {
      $('.bb-udiff').removeClass('collapsed');
    }
    $(this).find('.aui-icon').toggleClass('aui-iconfont-collapsed aui-iconfont-expanded');
  }
}

export default new TogglePanel();
<commit_msg>Fix: togglePanel wait for source load<commit_after>'use strict';

class TogglePanel {
  setEnabled(enabled = true) {
    this.enabled = enabled;
  }

  init() {
    if (!$('.source').length) {
      setTimeout(() => this.init(), 1000);
      return;
    }
    $('#compare').addClass('togglePanelWrapper');
    $('.diff-container .heading .primary').click(function() {
      $(this).parents('.bb-udiff').toggleClass('collapsed');
    });
  }

  getButton() {
    const toggleAllPanels = this.toggleAllPanels;
    if (!this.enabled) {
      return '';
    } else {
      const btn = $('<button id="togglePanels" class="bb_menu_item aui-button" />')
        .text('Panels')
        .prepend('')
        .click(toggleAllPanels);
      return btn;
    }
  }

  toggleAllPanels() {
    // TODO: Review the correct behavior of the toggle button, now the first defines.
    const collapse = !$('.bb-udiff:first').hasClass('collapsed');
    if (collapse) {
      $('.bb-udiff').addClass('collapsed');
    } else {
      $('.bb-udiff').removeClass('collapsed');
    }
    $(this).find('.aui-icon').toggleClass('aui-iconfont-collapsed aui-iconfont-expanded');
  }
}

export default new TogglePanel();
<|endoftext|>


<commit_before>var funkit = require('funkit');
var is = require('is-js');
var mongoose = require('mongoose');
var ObjectId = mongoose.Schema.ObjectId;

function refs(name) {
    return [{type: ObjectId, ref: name, validate: isValid}];
}
exports.refs = refs;

function isValid(n) {
    return ObjectId.isValid(n);
}

function schema(m) {
    return function(name, o) {
        o.created = {type: Date, 'default': Date.now};
        var meta = initMeta(o);
        o.deleted = {type: Boolean, 'default': false, select: false};

        var ret = m.model(name, new m.Schema(o, {strict: true}));
        ret.meta = meta;
        return ret;
    };
}
exports.schema = schema;

function initMeta(o) {
    var ret = {};

    console.log('init meta for', o);
    for(var k in o) {
        ret[k] = funkit.common.copy(o[k]);

        var v = ret[k];
        var type = v? v.type: v;
        if(is.fn(v)) ret[k] = {type: v.name};
        else if(is.array(v)) {
            ret[k][0] = funkit.common.copy(v[0]);
            ret[k][0].type = v[0].type.name;
        }
        else if(is.array(type)) ret[k].type = [type[0].name];
        else ret[k].type = type.name;
    }

    return ret;
}

function get(model, id, fields, cb) {
    model.findById(id, fields, cb);
}
exports.get = get;

function create(model, data, cb) {
    (new model(data)).save(cb);
}
exports.create = create;

function getAll(model, query, cb) {
    var fields = query.fields;
    var limit = query.limit;
    var skip = limit * query.offset;

    delete query.fields;
    delete query.limit;
    delete query.offset;

    model.find(query, fields, {limit: limit, skip: skip}).
        where('deleted', false).exec(cb);
}
exports.getAll = getAll;

function removeAll(model, cb) {
    model.remove().exec(cb);
}
exports.removeAll = removeAll;

function update(model, id, data, cb) {
    get(model, id, undefined, function(err, ob) {
        if(err) return cb(err);

        for(var k in data) ob[k] = data[k];

        return ob.save(cb);
    });
}
exports.update = update;

function del(model, id, cb) {
    update(model, id, {deleted: true}, cb);
}
exports['delete'] = del;

function count(model, cb) {
    model.count({}, cb);
}
exports.count = count;

function getMeta(model) {
    return model.meta;
}
exports.getMeta = getMeta;

<commit_msg>Remove debug print<commit_after>var funkit = require('funkit');
var is = require('is-js');
var mongoose = require('mongoose');
var ObjectId = mongoose.Schema.ObjectId;

function refs(name) {
    return [{type: ObjectId, ref: name, validate: isValid}];
}
exports.refs = refs;

function isValid(n) {
    return ObjectId.isValid(n);
}

function schema(m) {
    return function(name, o) {
        o.created = {type: Date, 'default': Date.now};
        var meta = initMeta(o);
        o.deleted = {type: Boolean, 'default': false, select: false};

        var ret = m.model(name, new m.Schema(o, {strict: true}));
        ret.meta = meta;
        return ret;
    };
}
exports.schema = schema;

function initMeta(o) {
    var ret = {};

    for(var k in o) {
        ret[k] = funkit.common.copy(o[k]);

        var v = ret[k];
        var type = v? v.type: v;
        if(is.fn(v)) ret[k] = {type: v.name};
        else if(is.array(v)) {
            ret[k][0] = funkit.common.copy(v[0]);
            ret[k][0].type = v[0].type.name;
        }
        else if(is.array(type)) ret[k].type = [type[0].name];
        else ret[k].type = type.name;
    }

    return ret;
}

function get(model, id, fields, cb) {
    model.findById(id, fields, cb);
}
exports.get = get;

function create(model, data, cb) {
    (new model(data)).save(cb);
}
exports.create = create;

function getAll(model, query, cb) {
    var fields = query.fields;
    var limit = query.limit;
    var skip = limit * query.offset;

    delete query.fields;
    delete query.limit;
    delete query.offset;

    model.find(query, fields, {limit: limit, skip: skip}).
        where('deleted', false).exec(cb);
}
exports.getAll = getAll;

function removeAll(model, cb) {
    model.remove().exec(cb);
}
exports.removeAll = removeAll;

function update(model, id, data, cb) {
    get(model, id, undefined, function(err, ob) {
        if(err) return cb(err);

        for(var k in data) ob[k] = data[k];

        return ob.save(cb);
    });
}
exports.update = update;

function del(model, id, cb) {
    update(model, id, {deleted: true}, cb);
}
exports['delete'] = del;

function count(model, cb) {
    model.count({}, cb);
}
exports.count = count;

function getMeta(model) {
    return model.meta;
}
exports.getMeta = getMeta;

<|endoftext|>


<commit_before>(function () {

  angular.module('ui.grid')
    .factory('GridApi', ['$q', '$rootScope', 'gridUtil', 'uiGridConstants', 'GridRow', 'uiGridGridMenuService',
      function ($q, $rootScope, gridUtil, uiGridConstants, GridRow, uiGridGridMenuService) {
        /**
         * @ngdoc function
         * @name ui.grid.class:GridApi
         * @description GridApi provides the ability to register public methods events inside the grid and allow
         * for other components to use the api via featureName.methodName and featureName.on.eventName(function(args){}
         * @param {object} grid grid that owns api
         */
        var GridApi = function GridApi(grid) {
          this.grid = grid;
          this.listeners = [];

          /**
           * @ngdoc function
           * @name renderingComplete
           * @methodOf  ui.grid.core.api:PublicApi
           * @description Rendering is complete, called at the same
           * time as `onRegisterApi`, but provides a way to obtain
           * that same event within features without stopping end
           * users from getting at the onRegisterApi method.
           *
           * Included in gridApi so that it's always there - otherwise
           * there is still a timing problem with when a feature can
           * call this.
           *
           * @param {GridApi} gridApi the grid api, as normally
           * returned in the onRegisterApi method
           *
           * @example
           *

           *      gridApi.core.on.renderingComplete( grid );
           *

           */
          this.registerEvent( 'core', 'renderingComplete' );

          /**
           * @ngdoc event
           * @name filterChanged
           * @eventOf  ui.grid.core.api:PublicApi
           * @description  is raised after the filter is changed.  The nature
           * of the watch expression doesn't allow notification of what changed,
           * so the receiver of this event will need to re-extract the filter
           * conditions from the columns.
           *
           */
          this.registerEvent( 'core', 'filterChanged' );

          /**
           * @ngdoc function
           * @name setRowInvisible
           * @methodOf  ui.grid.core.api:PublicApi
           * @description Sets an override on the row to make it always invisible,
           * which will override any filtering or other visibility calculations.
           * If the row is currently visible then sets it to invisible and calls
           * both grid refresh and emits the rowsVisibleChanged event
           * @param {object} rowEntity gridOptions.data[] array instance
           */
          this.registerMethod( 'core', 'setRowInvisible', GridRow.prototype.setRowInvisible );

          /**
           * @ngdoc function
           * @name clearRowInvisible
           * @methodOf  ui.grid.core.api:PublicApi
           * @description Clears any override on visibility for the row so that it returns to
           * using normal filtering and other visibility calculations.
           * If the row is currently invisible then sets it to visible and calls
           * both grid refresh and emits the rowsVisibleChanged event
           * TODO: if a filter is active then we can't just set it to visible?
           * @param {object} rowEntity gridOptions.data[] array instance
           */
          this.registerMethod( 'core', 'clearRowInvisible', GridRow.prototype.clearRowInvisible );

          /**
           * @ngdoc function
           * @name getVisibleRows
           * @methodOf  ui.grid.core.api:PublicApi
           * @description Returns all visible rows
           * @param {Grid} grid the grid you want to get visible rows from
           * @returns {array} an array of gridRow
           */
          this.registerMethod( 'core', 'getVisibleRows', this.grid.getVisibleRows );

          /**
           * @ngdoc event
           * @name rowsVisibleChanged
           * @eventOf  ui.grid.core.api:PublicApi
           * @description  is raised after the rows that are visible
           * change.  The filtering is zero-based, so it isn't possible
           * to say which rows changed (unlike in the selection feature).
           * We can plausibly know which row was changed when setRowInvisible
           * is called, but in that situation the user already knows which row
           * they changed.  When a filter runs we don't know what changed,
           * and that is the one that would have been useful.
           *
           */
          this.registerEvent( 'core', 'rowsVisibleChanged' );
        };

        /**
         * @ngdoc function
         * @name ui.grid.class:suppressEvents
         * @methodOf ui.grid.class:GridApi
         * @description Used to execute a function while disabling the specified event listeners.
         * Disables the listenerFunctions, executes the callbackFn, and then enables
         * the listenerFunctions again
         * @param {object} listenerFuncs listenerFunc or array of listenerFuncs to suppress. These must be the same
         * functions that were used in the .on.eventName method
         * @param {object} callBackFn function to execute
         * @example
         *

         *    var navigate = function (newRowCol, oldRowCol){
         *       //do something on navigate
         *    }
         *
         *    gridApi.cellNav.on.navigate(scope,navigate);
         *
         *
         *    //call the scrollTo event and suppress our navigate listener
         *    //scrollTo will still raise the event for other listeners
         *    gridApi.suppressEvents(navigate, function(){
         *       gridApi.cellNav.scrollTo(aRow, aCol);
         *    });
         *
         *

         */
        GridApi.prototype.suppressEvents = function (listenerFuncs, callBackFn) {
          var self = this;
          var listeners = angular.isArray(listenerFuncs) ? listenerFuncs : [listenerFuncs];

          //find all registered listeners
          var foundListeners = [];
          listeners.forEach(function (l) {
            foundListeners = self.listeners.filter(function (lstnr) {
              return l === lstnr.handler;
            });
          });

          //deregister all the listeners
          foundListeners.forEach(function(l){
            l.dereg();
          });

          callBackFn();

          //reregister all the listeners
          foundListeners.forEach(function(l){
              l.dereg = registerEventWithAngular(l.scope, l.eventId, l.handler, self.grid);
          });

        };

        /**
         * @ngdoc function
         * @name registerEvent
         * @methodOf ui.grid.class:GridApi
         * @description Registers a new event for the given feature
         * @param {string} featureName name of the feature that raises the event
         * @param {string} eventName  name of the event
         */
        GridApi.prototype.registerEvent = function (featureName, eventName) {
          var self = this;
          if (!self[featureName]) {
            self[featureName] = {};
          }

          var feature = self[featureName];
          if (!feature.on) {
            feature.on = {};
            feature.raise = {};
          }

          var eventId = self.grid.id + featureName + eventName;

          // gridUtil.logDebug('Creating raise event method ' + featureName + '.raise.' + eventName);
          feature.raise[eventName] = function () {
            $rootScope.$broadcast.apply($rootScope, [eventId].concat(Array.prototype.slice.call(arguments)));
          };

          // gridUtil.logDebug('Creating on event method ' + featureName + '.on.' + eventName);
          feature.on[eventName] = function (scope, handler) {
            var dereg = registerEventWithAngular(scope, eventId, handler, self.grid);

            //track our listener so we can turn off and on
            var listener = {handler: handler, dereg: dereg, eventId: eventId, scope: scope};
            self.listeners.push(listener);

            //destroy tracking when scope is destroyed
            //wanted to remove the listener from the array but angular does
            //strange things in scope.$destroy so I could not access the listener array
            scope.$on('$destroy', function() {
              listener.dereg = null;
              listener.handler = null;
              listener.eventId = null;
              listener.scope = null;
            });
          };
        };

        function registerEventWithAngular(scope, eventId, handler, grid) {
          return scope.$on(eventId, function (event) {
            var args = Array.prototype.slice.call(arguments);
            args.splice(0, 1); //remove evt argument
            handler.apply(grid.api, args);
          });
        }

        /**
         * @ngdoc function
         * @name registerEventsFromObject
         * @methodOf ui.grid.class:GridApi
         * @description Registers features and events from a simple objectMap.
         * eventObjectMap must be in this format (multiple features allowed)
         *

         * {featureName:
         *        {
         *          eventNameOne:function(args){},
         *          eventNameTwo:function(args){}
         *        }
         *  }
         *

         * @param {object} eventObjectMap map of feature/event names
         */
        GridApi.prototype.registerEventsFromObject = function (eventObjectMap) {
          var self = this;
          var features = [];
          angular.forEach(eventObjectMap, function (featProp, featPropName) {
            var feature = {name: featPropName, events: []};
            angular.forEach(featProp, function (prop, propName) {
              feature.events.push(propName);
            });
            features.push(feature);
          });

          features.forEach(function (feature) {
            feature.events.forEach(function (event) {
              self.registerEvent(feature.name, event);
            });
          });

        };

        /**
         * @ngdoc function
         * @name registerMethod
         * @methodOf ui.grid.class:GridApi
         * @description Registers a new event for the given feature
         * @param {string} featureName name of the feature
         * @param {string} methodName  name of the method
         * @param {object} callBackFn function to execute
         * @param {object} thisArg binds callBackFn 'this' to thisArg.  Defaults to gridApi.grid
         */
        GridApi.prototype.registerMethod = function (featureName, methodName, callBackFn, thisArg) {
          if (!this[featureName]) {
            this[featureName] = {};
          }

          var feature = this[featureName];

          feature[methodName] = gridUtil.createBoundedWrapper(thisArg || this.grid, callBackFn);
        };

        /**
         * @ngdoc function
         * @name registerMethodsFromObject
         * @methodOf ui.grid.class:GridApi
         * @description Registers features and methods from a simple objectMap.
         * eventObjectMap must be in this format (multiple features allowed)
         *

         * {featureName:
         *        {
         *          methodNameOne:function(args){},
         *          methodNameTwo:function(args){}
         *        }
         * @param {object} eventObjectMap map of feature/event names
         * @param {object} thisArg binds this to thisArg for all functions.  Defaults to gridApi.grid
         */
        GridApi.prototype.registerMethodsFromObject = function (methodMap, thisArg) {
          var self = this;
          var features = [];
          angular.forEach(methodMap, function (featProp, featPropName) {
            var feature = {name: featPropName, methods: []};
            angular.forEach(featProp, function (prop, propName) {
              feature.methods.push({name: propName, fn: prop});
            });
            features.push(feature);
          });

          features.forEach(function (feature) {
            feature.methods.forEach(function (method) {
              self.registerMethod(feature.name, method.name, method.fn, thisArg);
            });
          });

        };

        return GridApi;

      }]);

})();
<commit_msg>docs(GridApi) Improve docs and add example on how to subscribe to events<commit_after>(function () {

  angular.module('ui.grid')
    .factory('GridApi', ['$q', '$rootScope', 'gridUtil', 'uiGridConstants', 'GridRow', 'uiGridGridMenuService',
      function ($q, $rootScope, gridUtil, uiGridConstants, GridRow, uiGridGridMenuService) {
        /**
         * @ngdoc function
         * @name ui.grid.class:GridApi
         * @description GridApi provides the ability to register public methods events inside the grid and allow
         * for other components to use the api via featureName.raise.methodName and featureName.on.eventName(function(args){}.
         *

         * To listen to events, you must add a callback to gridOptions.onRegisterApi
         *

         *   $scope.gridOptions.onRegisterApi = function(gridApi){
         *      gridApi.cellNav.on.navigate($scope,function(newRowCol, oldRowCol){
         *          $log.log('navigation event');
         *      });
         *   };
         *

         * @param {object} grid grid that owns api
         */
        var GridApi = function GridApi(grid) {
          this.grid = grid;
          this.listeners = [];

          /**
           * @ngdoc function
           * @name renderingComplete
           * @methodOf  ui.grid.core.api:PublicApi
           * @description Rendering is complete, called at the same
           * time as `onRegisterApi`, but provides a way to obtain
           * that same event within features without stopping end
           * users from getting at the onRegisterApi method.
           *
           * Included in gridApi so that it's always there - otherwise
           * there is still a timing problem with when a feature can
           * call this.
           *
           * @param {GridApi} gridApi the grid api, as normally
           * returned in the onRegisterApi method
           *
           * @example
           *

           *      gridApi.core.on.renderingComplete( grid );
           *

           */
          this.registerEvent( 'core', 'renderingComplete' );

          /**
           * @ngdoc event
           * @name filterChanged
           * @eventOf  ui.grid.core.api:PublicApi
           * @description  is raised after the filter is changed.  The nature
           * of the watch expression doesn't allow notification of what changed,
           * so the receiver of this event will need to re-extract the filter
           * conditions from the columns.
           *
           */
          this.registerEvent( 'core', 'filterChanged' );

          /**
           * @ngdoc function
           * @name setRowInvisible
           * @methodOf  ui.grid.core.api:PublicApi
           * @description Sets an override on the row to make it always invisible,
           * which will override any filtering or other visibility calculations.
           * If the row is currently visible then sets it to invisible and calls
           * both grid refresh and emits the rowsVisibleChanged event
           * @param {object} rowEntity gridOptions.data[] array instance
           */
          this.registerMethod( 'core', 'setRowInvisible', GridRow.prototype.setRowInvisible );

          /**
           * @ngdoc function
           * @name clearRowInvisible
           * @methodOf  ui.grid.core.api:PublicApi
           * @description Clears any override on visibility for the row so that it returns to
           * using normal filtering and other visibility calculations.
           * If the row is currently invisible then sets it to visible and calls
           * both grid refresh and emits the rowsVisibleChanged event
           * TODO: if a filter is active then we can't just set it to visible?
           * @param {object} rowEntity gridOptions.data[] array instance
           */
          this.registerMethod( 'core', 'clearRowInvisible', GridRow.prototype.clearRowInvisible );

          /**
           * @ngdoc function
           * @name getVisibleRows
           * @methodOf  ui.grid.core.api:PublicApi
           * @description Returns all visible rows
           * @param {Grid} grid the grid you want to get visible rows from
           * @returns {array} an array of gridRow
           */
          this.registerMethod( 'core', 'getVisibleRows', this.grid.getVisibleRows );

          /**
           * @ngdoc event
           * @name rowsVisibleChanged
           * @eventOf  ui.grid.core.api:PublicApi
           * @description  is raised after the rows that are visible
           * change.  The filtering is zero-based, so it isn't possible
           * to say which rows changed (unlike in the selection feature).
           * We can plausibly know which row was changed when setRowInvisible
           * is called, but in that situation the user already knows which row
           * they changed.  When a filter runs we don't know what changed,
           * and that is the one that would have been useful.
           *
           */
          this.registerEvent( 'core', 'rowsVisibleChanged' );
        };

        /**
         * @ngdoc function
         * @name ui.grid.class:suppressEvents
         * @methodOf ui.grid.class:GridApi
         * @description Used to execute a function while disabling the specified event listeners.
         * Disables the listenerFunctions, executes the callbackFn, and then enables
         * the listenerFunctions again
         * @param {object} listenerFuncs listenerFunc or array of listenerFuncs to suppress. These must be the same
         * functions that were used in the .on.eventName method
         * @param {object} callBackFn function to execute
         * @example
         *

         *    var navigate = function (newRowCol, oldRowCol){
         *       //do something on navigate
         *    }
         *
         *    gridApi.cellNav.on.navigate(scope,navigate);
         *
         *
         *    //call the scrollTo event and suppress our navigate listener
         *    //scrollTo will still raise the event for other listeners
         *    gridApi.suppressEvents(navigate, function(){
         *       gridApi.cellNav.scrollTo(aRow, aCol);
         *    });
         *
         *

         */
        GridApi.prototype.suppressEvents = function (listenerFuncs, callBackFn) {
          var self = this;
          var listeners = angular.isArray(listenerFuncs) ? listenerFuncs : [listenerFuncs];

          //find all registered listeners
          var foundListeners = [];
          listeners.forEach(function (l) {
            foundListeners = self.listeners.filter(function (lstnr) {
              return l === lstnr.handler;
            });
          });

          //deregister all the listeners
          foundListeners.forEach(function(l){
            l.dereg();
          });

          callBackFn();

          //reregister all the listeners
          foundListeners.forEach(function(l){
              l.dereg = registerEventWithAngular(l.scope, l.eventId, l.handler, self.grid);
          });

        };

        /**
         * @ngdoc function
         * @name registerEvent
         * @methodOf ui.grid.class:GridApi
         * @description Registers a new event for the given feature.  The event will get a
         * .raise and .on prepended to it
         * @param {string} featureName name of the feature that raises the event
         * @param {string} eventName  name of the event
         */
        GridApi.prototype.registerEvent = function (featureName, eventName) {
          var self = this;
          if (!self[featureName]) {
            self[featureName] = {};
          }

          var feature = self[featureName];
          if (!feature.on) {
            feature.on = {};
            feature.raise = {};
          }

          var eventId = self.grid.id + featureName + eventName;

          // gridUtil.logDebug('Creating raise event method ' + featureName + '.raise.' + eventName);
          feature.raise[eventName] = function () {
            $rootScope.$broadcast.apply($rootScope, [eventId].concat(Array.prototype.slice.call(arguments)));
          };

          // gridUtil.logDebug('Creating on event method ' + featureName + '.on.' + eventName);
          feature.on[eventName] = function (scope, handler) {
            var dereg = registerEventWithAngular(scope, eventId, handler, self.grid);

            //track our listener so we can turn off and on
            var listener = {handler: handler, dereg: dereg, eventId: eventId, scope: scope};
            self.listeners.push(listener);

            //destroy tracking when scope is destroyed
            //wanted to remove the listener from the array but angular does
            //strange things in scope.$destroy so I could not access the listener array
            scope.$on('$destroy', function() {
              listener.dereg = null;
              listener.handler = null;
              listener.eventId = null;
              listener.scope = null;
            });
          };
        };

        function registerEventWithAngular(scope, eventId, handler, grid) {
          return scope.$on(eventId, function (event) {
            var args = Array.prototype.slice.call(arguments);
            args.splice(0, 1); //remove evt argument
            handler.apply(grid.api, args);
          });
        }

        /**
         * @ngdoc function
         * @name registerEventsFromObject
         * @methodOf ui.grid.class:GridApi
         * @description Registers features and events from a simple objectMap.
         * eventObjectMap must be in this format (multiple features allowed)
         *

         * {featureName:
         *        {
         *          eventNameOne:function(args){},
         *          eventNameTwo:function(args){}
         *        }
         *  }
         *

         * @param {object} eventObjectMap map of feature/event names
         */
        GridApi.prototype.registerEventsFromObject = function (eventObjectMap) {
          var self = this;
          var features = [];
          angular.forEach(eventObjectMap, function (featProp, featPropName) {
            var feature = {name: featPropName, events: []};
            angular.forEach(featProp, function (prop, propName) {
              feature.events.push(propName);
            });
            features.push(feature);
          });

          features.forEach(function (feature) {
            feature.events.forEach(function (event) {
              self.registerEvent(feature.name, event);
            });
          });

        };

        /**
         * @ngdoc function
         * @name registerMethod
         * @methodOf ui.grid.class:GridApi
         * @description Registers a new event for the given feature
         * @param {string} featureName name of the feature
         * @param {string} methodName  name of the method
         * @param {object} callBackFn function to execute
         * @param {object} thisArg binds callBackFn 'this' to thisArg.  Defaults to gridApi.grid
         */
        GridApi.prototype.registerMethod = function (featureName, methodName, callBackFn, thisArg) {
          if (!this[featureName]) {
            this[featureName] = {};
          }

          var feature = this[featureName];

          feature[methodName] = gridUtil.createBoundedWrapper(thisArg || this.grid, callBackFn);
        };

        /**
         * @ngdoc function
         * @name registerMethodsFromObject
         * @methodOf ui.grid.class:GridApi
         * @description Registers features and methods from a simple objectMap.
         * eventObjectMap must be in this format (multiple features allowed)
         *

         * {featureName:
         *        {
         *          methodNameOne:function(args){},
         *          methodNameTwo:function(args){}
         *        }
         * @param {object} eventObjectMap map of feature/event names
         * @param {object} thisArg binds this to thisArg for all functions.  Defaults to gridApi.grid
         */
        GridApi.prototype.registerMethodsFromObject = function (methodMap, thisArg) {
          var self = this;
          var features = [];
          angular.forEach(methodMap, function (featProp, featPropName) {
            var feature = {name: featPropName, methods: []};
            angular.forEach(featProp, function (prop, propName) {
              feature.methods.push({name: propName, fn: prop});
            });
            features.push(feature);
          });

          features.forEach(function (feature) {
            feature.methods.forEach(function (method) {
              self.registerMethod(feature.name, method.name, method.fn, thisArg);
            });
          });

        };

        return GridApi;

      }]);

})();
<|endoftext|>


<commit_before>/*jshint node:true*/
"use strict";

/*!
 * Search query helpers
 */

var queryRegex = /^\/(.*)\/([imx]*)$/;

/* Generate a mongoose query operator from a ?query= request parameter */
function createQueryOperator(query) {
	var or = {
		$or: query.split(" OR ").map(function(orOperand) {
			var and = {
				$and: orOperand.split(" AND ").map(function(andOperand) {
					var colonIndex = andOperand.indexOf(":"),
						bangIndex = andOperand.indexOf("!");

					if (colonIndex === -1 && bangIndex === -1) {
						// Invalid operator, skip
						return {};
					}

					var negate = colonIndex === -1 || (bangIndex !== -1 && bangIndex < colonIndex),
						split = andOperand.split(negate ? "!" : ":"),
						field = split[0],
						value = split[1],
						operator = {},
						op, matches;

					matches = value.match(queryRegex);

					if (matches) {
						op = new RegExp(matches[1], matches[2]);
						operator[field] = negate ? { $not: op } : op;
					} else {
						if (negate) {
							// Mongoose does not handle { $not: "value" }
							operator[field] = { $nin: [value] };
						} else {
							operator[field] = value;
						}
					}

					return operator;
				}).filter(function(operator) {
					return Object.keys(operator).length > 0;
				})
			};

			return and.$and.length === 1 ? and.$and[0] : and;
		})
	};

	return or.$or.length === 1 ? or.$or[0] : or;
}

/* Get property path value in a document or in a plain object */
function getPath(obj, path) {
	if (typeof obj.get === "function") {
		return obj.get(path);
	}

	var parts = path.split(".");

	while (parts.length) {
		if (!obj) {
			return;
		}

		obj = obj[parts.shift()];
	}

	return obj;
}

/* Match a mongoose query criterion to a document */
function matchQueryCriterion(crit, doc) {
	return Object.keys(crit).every(function(path) {
		var value = getPath(doc, path) || "",
			match = crit[path],
			negate = false,
			result;

		if (typeof match === "string") {
			result = value.toString() === match;
		} else {
			if ("$not" in match) {
				negate = true;
				match = match.$not;
			}

			if (match instanceof RegExp) {
				result = !!value.toString().match(match);
			} else if ("$nin" in match) {
				result = match.$nin.indexOf(value) === -1;
			} else {
				return false;
			}
		}

		return negate ? !result : result;
	});
}

/* Match a mongoose query operator to a document */
function matchQueryOperator(operator, doc) {
	if ("$or" in operator) {
		return operator.$or.some(function(op) {
			return matchQueryOperator(op, doc);
		});
	} else if ("$and" in operator) {
		return operator.$and.every(function(op) {
			return matchQueryOperator(op, doc);
		});
	} else if ("$not" in operator) {
		return !matchQueryOperator(operator.$not, doc);
	} else {
		return matchQueryCriterion(operator, doc);
	}
}

module.exports = {
	create: createQueryOperator,
	match: matchQueryOperator
};<commit_msg>Fix colon usage in query values<commit_after>/*jshint node:true*/
"use strict";

/*!
 * Search query helpers
 */

var queryRegex = /^\/(.*)\/([imx]*)$/;

/* Generate a mongoose query operator from a ?query= request parameter */
function createQueryOperator(query) {
	var or = {
		$or: query.split(" OR ").map(function(orOperand) {
			var and = {
				$and: orOperand.split(" AND ").map(function(andOperand) {
					var colonIndex = andOperand.indexOf(":"),
						bangIndex = andOperand.indexOf("!");

					if (colonIndex === -1 && bangIndex === -1) {
						// Invalid operator, skip
						return {};
					}

					var match = andOperand.match(/^([^!:]+)([!:])(.*)$/);
					var field = match[1];
					var negate = match[2] === "!";
					var value = match[3];
					var operator = {};
					var op, matches;

					matches = value.match(queryRegex);

					if (matches) {
						op = new RegExp(matches[1], matches[2]);
						operator[field] = negate ? { $not: op } : op;
					} else {
						if (negate) {
							// Mongoose does not handle { $not: "value" }
							operator[field] = { $nin: [value] };
						} else {
							operator[field] = value;
						}
					}

					return operator;
				}).filter(function(operator) {
					return Object.keys(operator).length > 0;
				})
			};

			return and.$and.length === 1 ? and.$and[0] : and;
		})
	};

	return or.$or.length === 1 ? or.$or[0] : or;
}

/* Get property path value in a document or in a plain object */
function getPath(obj, path) {
	if (typeof obj.get === "function") {
		return obj.get(path);
	}

	var parts = path.split(".");

	while (parts.length) {
		if (!obj) {
			return;
		}

		obj = obj[parts.shift()];
	}

	return obj;
}

/* Match a mongoose query criterion to a document */
function matchQueryCriterion(crit, doc) {
	return Object.keys(crit).every(function(path) {
		var value = getPath(doc, path) || "",
			match = crit[path],
			negate = false,
			result;

		if (typeof match === "string") {
			result = value.toString() === match;
		} else {
			if ("$not" in match) {
				negate = true;
				match = match.$not;
			}

			if (match instanceof RegExp) {
				result = !!value.toString().match(match);
			} else if ("$nin" in match) {
				result = match.$nin.indexOf(value) === -1;
			} else {
				return false;
			}
		}

		return negate ? !result : result;
	});
}

/* Match a mongoose query operator to a document */
function matchQueryOperator(operator, doc) {
	if ("$or" in operator) {
		return operator.$or.some(function(op) {
			return matchQueryOperator(op, doc);
		});
	} else if ("$and" in operator) {
		return operator.$and.every(function(op) {
			return matchQueryOperator(op, doc);
		});
	} else if ("$not" in operator) {
		return !matchQueryOperator(operator.$not, doc);
	} else {
		return matchQueryCriterion(operator, doc);
	}
}

module.exports = {
	create: createQueryOperator,
	match: matchQueryOperator
};<|endoftext|>


<commit_before>(function($) {

	$.extend(mejs.MepDefaults, {
		forcePluginFullScreen: false,
		newWindowCallback: function() { return '';},
		fullscreenText: 'Fullscreen'
	});

	$.extend(MediaElementPlayer.prototype, {

		isFullScreen: false,

		isNativeFullScreen: false,

		docStyleOverflow: null,

		isInIframe: false,

		buildfullscreen: function(player, controls, layers, media) {

			if (!player.isVideo)
				return;

			player.isInIframe = (window.location != window.parent.location);

			// native events
			if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
				console.log('added change event: ' + mejs.MediaFeatures.fullScreenEventName);

				player.container.bind(mejs.MediaFeatures.fullScreenEventName, function(e) {
				//player.container.bind('webkitfullscreenchange', function(e) {

					console.log('fullscreenchange event: ' + mejs.MediaFeatures.isFullScreen());

					if (mejs.MediaFeatures.isFullScreen()) {
						player.isNativeFullScreen = true;
						// reset the controls once we are fully in full screen
						player.setControlsSize();
					} else {
						player.isNativeFullScreen = false;
						// when a user presses ESC
						// make sure to put the player back into place
						player.exitFullScreen();
					}
				});
			}

			var t = this,
				normalHeight = 0,
				normalWidth = 0,
				container = player.container,
				fullscreenBtn =
					$('
' +
						'<button type="button" aria-controls="' + t.id + '" title="' + t.options.fullscreenText + '"></button>' +
					'
')
					.appendTo(controls)
					.click(function() {
						var isFullScreen = (mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || player.isFullScreen;

						if (isFullScreen) {
							player.exitFullScreen();
						} else {
							player.enterFullScreen();
						}
					});

			player.fullscreenBtn = fullscreenBtn;

			$(document).bind('keydown',function (e) {
				if (((mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || t.isFullScreen) && e.keyCode == 27) {
					player.exitFullScreen();
				}
			});

		},
		enterFullScreen: function() {

			var t = this;

			// firefox+flash can't adjust plugin sizes without resetting :(
			if (t.media.pluginType !== 'native' && (mejs.MediaFeatures.isGecko || t.options.forcePluginFullScreen)) {
				t.media.setFullscreen(true);
				//player.isFullScreen = true;
				return;
			}

			// store overflow
			docStyleOverflow = document.documentElement.style.overflow;
			// set it to not show scroll bars so 100% will work
			document.documentElement.style.overflow = 'hidden';

			// store sizing
			normalHeight = t.container.height();
			normalWidth = t.container.width();

			console.log('true: ' + mejs.MediaFeatures.hasTrueNativeFullScreen + ', semi: ' + mejs.MediaFeatures.hasSemiNativeFullScreen)

			// attempt to do true fullscreen (Safari 5.1 and Firefox Nightly only for now)
			if (mejs.MediaFeatures.hasTrueNativeFullScreen) {

				mejs.MediaFeatures.requestFullScreen(t.container[0]);
				//return;

			} else if (mejs.MediaFeatures.hasSemiNativeFullScreen) {
				t.media.webkitEnterFullscreen();
				return;
			}

			// check for iframe launch
			if (t.isInIframe) {
				var url = t.options.newWindowCallback(this);

				if (url !== '') {

					// launch immediately
					if (!mejs.MediaFeatures.hasTrueNativeFullScreen) {
						t.pause();
						window.open(url, t.id, 'top=0,left=0,width=' + screen.availWidth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no');
						return;
					} else {
						setTimeout(function() {
							if (!t.isNativeFullScreen) {
								t.pause();
								window.open(url, t.id, 'top=0,left=0,width=' + screen.availWidth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no');
							}
						}, 250);
					}
				}

			}

			// full window code

			// make full size
			t.container
				.addClass('mejs-container-fullscreen')
				.width('100%')
				.height('100%');
				//.css({position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, overflow: 'hidden', width: '100%', height: '100%', 'z-index': 1000});

			// Only needed for safari 5.1 native full screen, can cause display issues elsewhere
			if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
				setTimeout(function() {
					t.container.css({width: '100%', height: '100%'});
				}, 500);
			}

			if (t.pluginType === 'native') {
				t.$media
					.width('100%')
					.height('100%');
			} else {
				t.container.find('object embed')
					.width('100%')
					.height('100%');
				t.media.setVideoSize($(window).width(),$(window).height());
			}

			t.layers.children('div')
				.width('100%')
				.height('100%');

			if (t.fullscreenBtn) {
				t.fullscreenBtn
					.removeClass('mejs-fullscreen')
					.addClass('mejs-unfullscreen');
			}

			t.setControlsSize();
			t.isFullScreen = true;
		},

		exitFullScreen: function() {

			var t = this;

			// firefox can't adjust plugins
			if (t.media.pluginType !== 'native' && mejs.MediaFeatures.isFirefox) {
				t.media.setFullscreen(false);
				//player.isFullScreen = false;
				return;
			}

			// come outo of native fullscreen
			if (mejs.MediaFeatures.hasTrueNativeFullScreen && (mejs.MediaFeatures.isFullScreen() || t.isFullScreen)) {
				mejs.MediaFeatures.cancelFullScreen();
			}

			// restore scroll bars to document
			document.documentElement.style.overflow = docStyleOverflow;

			t.container
				.removeClass('mejs-container-fullscreen')
				.width(normalWidth)
				.height(normalHeight);
				//.css({position: '', left: '', top: '', right: '', bottom: '', overflow: 'inherit', width: normalWidth + 'px', height: normalHeight + 'px', 'z-index': 1});

			if (t.pluginType === 'native') {
				t.$media
					.width(normalWidth)
					.height(normalHeight);
			} else {
				t.container.find('object embed')
					.width(normalWidth)
					.height(normalHeight);

				t.media.setVideoSize(normalWidth, normalHeight);
			}

			t.layers.children('div')
				.width(normalWidth)
				.height(normalHeight);

			t.fullscreenBtn
				.removeClass('mejs-unfullscreen')
				.addClass('mejs-fullscreen');

			t.setControlsSize();
			t.isFullScreen = false;
		}
	});

})(mejs.$);
<commit_msg>removed console.log<commit_after>(function($) {

	$.extend(mejs.MepDefaults, {
		forcePluginFullScreen: false,
		newWindowCallback: function() { return '';},
		fullscreenText: 'Fullscreen'
	});

	$.extend(MediaElementPlayer.prototype, {

		isFullScreen: false,

		isNativeFullScreen: false,

		docStyleOverflow: null,

		isInIframe: false,

		buildfullscreen: function(player, controls, layers, media) {

			if (!player.isVideo)
				return;

			player.isInIframe = (window.location != window.parent.location);

			// native events
			if (mejs.MediaFeatures.hasTrueNativeFullScreen) {

				player.container.bind(mejs.MediaFeatures.fullScreenEventName, function(e) {
				//player.container.bind('webkitfullscreenchange', function(e) {

					if (mejs.MediaFeatures.isFullScreen()) {
						player.isNativeFullScreen = true;
						// reset the controls once we are fully in full screen
						player.setControlsSize();
					} else {
						player.isNativeFullScreen = false;
						// when a user presses ESC
						// make sure to put the player back into place
						player.exitFullScreen();
					}
				});
			}

			var t = this,
				normalHeight = 0,
				normalWidth = 0,
				container = player.container,
				fullscreenBtn =
					$('
' +
						'<button type="button" aria-controls="' + t.id + '" title="' + t.options.fullscreenText + '"></button>' +
					'
')
					.appendTo(controls)
					.click(function() {
						var isFullScreen = (mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || player.isFullScreen;

						if (isFullScreen) {
							player.exitFullScreen();
						} else {
							player.enterFullScreen();
						}
					});

			player.fullscreenBtn = fullscreenBtn;

			$(document).bind('keydown',function (e) {
				if (((mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || t.isFullScreen) && e.keyCode == 27) {
					player.exitFullScreen();
				}
			});

		},
		enterFullScreen: function() {

			var t = this;

			// firefox+flash can't adjust plugin sizes without resetting :(
			if (t.media.pluginType !== 'native' && (mejs.MediaFeatures.isGecko || t.options.forcePluginFullScreen)) {
				t.media.setFullscreen(true);
				//player.isFullScreen = true;
				return;
			}

			// store overflow
			docStyleOverflow = document.documentElement.style.overflow;
			// set it to not show scroll bars so 100% will work
			document.documentElement.style.overflow = 'hidden';

			// store sizing
			normalHeight = t.container.height();
			normalWidth = t.container.width();

			// attempt to do true fullscreen (Safari 5.1 and Firefox Nightly only for now)
			if (mejs.MediaFeatures.hasTrueNativeFullScreen) {

				mejs.MediaFeatures.requestFullScreen(t.container[0]);
				//return;

			} else if (mejs.MediaFeatures.hasSemiNativeFullScreen) {
				t.media.webkitEnterFullscreen();
				return;
			}

			// check for iframe launch
			if (t.isInIframe) {
				var url = t.options.newWindowCallback(this);

				if (url !== '') {

					// launch immediately
					if (!mejs.MediaFeatures.hasTrueNativeFullScreen) {
						t.pause();
						window.open(url, t.id, 'top=0,left=0,width=' + screen.availWidth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no');
						return;
					} else {
						setTimeout(function() {
							if (!t.isNativeFullScreen) {
								t.pause();
								window.open(url, t.id, 'top=0,left=0,width=' + screen.availWidth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no');
							}
						}, 250);
					}
				}

			}

			// full window code

			// make full size
			t.container
				.addClass('mejs-container-fullscreen')
				.width('100%')
				.height('100%');
				//.css({position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, overflow: 'hidden', width: '100%', height: '100%', 'z-index': 1000});

			// Only needed for safari 5.1 native full screen, can cause display issues elsewhere
			if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
				setTimeout(function() {
					t.container.css({width: '100%', height: '100%'});
				}, 500);
			}

			if (t.pluginType === 'native') {
				t.$media
					.width('100%')
					.height('100%');
			} else {
				t.container.find('object embed')
					.width('100%')
					.height('100%');
				t.media.setVideoSize($(window).width(),$(window).height());
			}

			t.layers.children('div')
				.width('100%')
				.height('100%');

			if (t.fullscreenBtn) {
				t.fullscreenBtn
					.removeClass('mejs-fullscreen')
					.addClass('mejs-unfullscreen');
			}

			t.setControlsSize();
			t.isFullScreen = true;
		},

		exitFullScreen: function() {

			var t = this;

			// firefox can't adjust plugins
			if (t.media.pluginType !== 'native' && mejs.MediaFeatures.isFirefox) {
				t.media.setFullscreen(false);
				//player.isFullScreen = false;
				return;
			}

			// come outo of native fullscreen
			if (mejs.MediaFeatures.hasTrueNativeFullScreen && (mejs.MediaFeatures.isFullScreen() || t.isFullScreen)) {
				mejs.MediaFeatures.cancelFullScreen();
			}

			// restore scroll bars to document
			document.documentElement.style.overflow = docStyleOverflow;

			t.container
				.removeClass('mejs-container-fullscreen')
				.width(normalWidth)
				.height(normalHeight);
				//.css({position: '', left: '', top: '', right: '', bottom: '', overflow: 'inherit', width: normalWidth + 'px', height: normalHeight + 'px', 'z-index': 1});

			if (t.pluginType === 'native') {
				t.$media
					.width(normalWidth)
					.height(normalHeight);
			} else {
				t.container.find('object embed')
					.width(normalWidth)
					.height(normalHeight);

				t.media.setVideoSize(normalWidth, normalHeight);
			}

			t.layers.children('div')
				.width(normalWidth)
				.height(normalHeight);

			t.fullscreenBtn
				.removeClass('mejs-unfullscreen')
				.addClass('mejs-fullscreen');

			t.setControlsSize();
			t.isFullScreen = false;
		}
	});

})(mejs.$);
<|endoftext|>


<commit_before>class PageAction {
    /**
     * Initialize instance, watch tabs navigation and options changes.
     * @param options
     * @param remotePontoon
     */
    constructor(options, remotePontoon) {
        this._options = options;
        this._remotePontoon = remotePontoon;

        this._watchOptionsUpdates();
        this._watchStorageChanges();
        this._watchTabsUpdates();
        this._listenToMessagesFromPageAction();
        this._refreshAllTabsPageAction();
    }

    /**
     * Keep click action in sync with options.
     * @private
     */
    _watchOptionsUpdates() {
        this._options.subscribeToOptionChange('display_page_action', (change) =>
            this._refreshAllTabsPageAction(change.newValue)
        );
    }

    /**
     * Update page actions when the list of projects changes.
     * @private
     */
    _watchStorageChanges() {
        this._remotePontoon.subscribeToProjectsListChange(
            (change) => this._refreshAllTabsPageAction()
        );
    }

    /**
     * Listen to messages from opened page action menus
     * @private
     */
    _listenToMessagesFromPageAction() {
        browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
            switch (request.type) {
                case 'page-action-opened':
                    browser.tabs.query({currentWindow: true, active: true}).then((tab) => {
                        this._getPontoonProjectForPageUrl(tab[0].url).then((projectData) => {
                            // NOTE: The request-response messaging does not work here, because we are in async code here.
                            browser.runtime.sendMessage({
                                type: 'page-action-project-data',
                                project: {
                                    name: projectData.name,
                                    pageUrl: this._remotePontoon.getTeamProjectUrl(`/projects/${projectData.slug}/`),
                                    translationUrl: this._remotePontoon.getTeamProjectUrl(`/projects/${projectData.slug}/all-resources/`),
                                }
                            });
                        });
                    });
                    break;
            }
        });
    }

    /**
     * Show page action in recognized tabs.
     * @private
     */
    _watchTabsUpdates() {
        browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
            if (changeInfo.status === 'complete') {
                this._showPageActionForTab(tab);
            }
        });
    }

    /**
     * Refresh page action for all tab.
     * @param show optional boolean to force show (true) or hide (false)
     * @private
     */
    _refreshAllTabsPageAction(show) {
        const optionKey = 'display_page_action';
        Promise.all([
            browser.tabs.query({}),
            this._options.get(optionKey)
        ]).then(([
            tabs,
            optionItem
        ]) => {
            if (show === undefined) {
                tabs.forEach((tab) => this._showPageActionForTab(tab, optionItem[optionKey]));
            } else {
                tabs.forEach((tab) => this._showPageActionForTab(tab, show));
            }
        });
    }

    /**
     * Show page action if the tab is recognized.
     * @param tab to recognize and show page action for
     * @param show optional boolean to force show (true) or hide (false)
     * @private
     */
    async _showPageActionForTab(tab, show) {
        const projectData = await this._getPontoonProjectForPageUrl(tab.url);
        if (projectData) {
            if (show === undefined) {
                const optionKey = 'display_page_action';
                show = (await this._options.get(optionKey))[optionKey];
            }
            if(show) {
                this._activatePageAction(tab.id);
                browser.pageAction.setTitle({tabId: tab.id, title: `Open ${projectData.name} in Pontoon`});
            } else {
                this._deactivatePageAction(tab.id);
                browser.pageAction.setTitle({tabId: tab.id, title: 'Pontoon Tools page action is disabled'});
            }
        } else {
            this._deactivatePageAction(tab.id);
            browser.pageAction.setTitle({tabId: tab.id, title: 'No project for this page'});
        }
    }

    /**
     * Activate the page action for tab with the right icon.
     * @param tabId to show the page action for
     * @private
     */
    _activatePageAction(tabId) {
        browser.pageAction.setIcon({
            path: {
              16: "img/pontoon-logo.svg",
              32: "img/pontoon-logo.svg"
            },
            tabId: tabId,
        });
        browser.pageAction.show(tabId);
    }

    /**
     * Show the page action for tab with the right icon.
     * @param tabId to show the page action for
     * @private
     */
    _deactivatePageAction(tabId) {
        browser.pageAction.setIcon({
            path: {
                16: "img/pontoon-logo-gray-alpha.svg",
                32: "img/pontoon-logo-gray-alpha.svg"
            },
            tabId: tabId,
        });
        browser.pageAction.hide(tabId);
    }

    /**
     * Get Pontoon project for the loaded page.
     * @param pageUrl loaded in a tab
     * @returns {object|undefined}
     * @private
     */
    async _getPontoonProjectForPageUrl(pageUrl) {
        const tmpLink = document.createElement('a');
        tmpLink.href = pageUrl;
        const toProjectMap = new Map();
        const dataKey = 'projectsList';
        await browser.storage.local.get(dataKey).then((item) => {
            if (item[dataKey]) {
                Object.values(item[dataKey]).forEach((project) =>
                    project.domains.forEach((domain) => toProjectMap.set(domain, project))
                );
            }
        });
        return toProjectMap.get(tmpLink.hostname);
    }
}<commit_msg>Documentation of page-action objects, #58<commit_after>/**
 * Runs in the background and takes care of proper page actions displaying and project data.
 */
class PageAction {
    /**
     * Initialize instance, watch tabs navigation and options changes.
     * @param options
     * @param remotePontoon
     */
    constructor(options, remotePontoon) {
        this._options = options;
        this._remotePontoon = remotePontoon;

        this._watchOptionsUpdates();
        this._watchStorageChanges();
        this._watchTabsUpdates();
        this._listenToMessagesFromPageAction();
        this._refreshAllTabsPageAction();
    }

    /**
     * Keep click action in sync with options.
     * @private
     */
    _watchOptionsUpdates() {
        this._options.subscribeToOptionChange('display_page_action', (change) =>
            this._refreshAllTabsPageAction(change.newValue)
        );
    }

    /**
     * Update page actions when the list of projects changes.
     * @private
     */
    _watchStorageChanges() {
        this._remotePontoon.subscribeToProjectsListChange(
            (change) => this._refreshAllTabsPageAction()
        );
    }

    /**
     * Listen to messages from opened page action menus
     * @private
     */
    _listenToMessagesFromPageAction() {
        browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
            switch (request.type) {
                case 'page-action-opened':
                    browser.tabs.query({currentWindow: true, active: true}).then((tab) => {
                        this._getPontoonProjectForPageUrl(tab[0].url).then((projectData) => {
                            // NOTE: The request-response messaging does not work here, because we are in async code here.
                            browser.runtime.sendMessage({
                                type: 'page-action-project-data',
                                project: {
                                    name: projectData.name,
                                    pageUrl: this._remotePontoon.getTeamProjectUrl(`/projects/${projectData.slug}/`),
                                    translationUrl: this._remotePontoon.getTeamProjectUrl(`/projects/${projectData.slug}/all-resources/`),
                                }
                            });
                        });
                    });
                    break;
            }
        });
    }

    /**
     * Show page action in recognized tabs.
     * @private
     */
    _watchTabsUpdates() {
        browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
            if (changeInfo.status === 'complete') {
                this._showPageActionForTab(tab);
            }
        });
    }

    /**
     * Refresh page action for all tab.
     * @param show optional boolean to force show (true) or hide (false)
     * @private
     */
    _refreshAllTabsPageAction(show) {
        const optionKey = 'display_page_action';
        Promise.all([
            browser.tabs.query({}),
            this._options.get(optionKey)
        ]).then(([
            tabs,
            optionItem
        ]) => {
            if (show === undefined) {
                tabs.forEach((tab) => this._showPageActionForTab(tab, optionItem[optionKey]));
            } else {
                tabs.forEach((tab) => this._showPageActionForTab(tab, show));
            }
        });
    }

    /**
     * Show page action if the tab is recognized.
     * @param tab to recognize and show page action for
     * @param show optional boolean to force show (true) or hide (false)
     * @private
     * @async
     */
    async _showPageActionForTab(tab, show) {
        const projectData = await this._getPontoonProjectForPageUrl(tab.url);
        if (projectData) {
            if (show === undefined) {
                const optionKey = 'display_page_action';
                show = (await this._options.get(optionKey))[optionKey];
            }
            if(show) {
                this._activatePageAction(tab.id);
                browser.pageAction.setTitle({tabId: tab.id, title: `Open ${projectData.name} in Pontoon`});
            } else {
                this._deactivatePageAction(tab.id);
                browser.pageAction.setTitle({tabId: tab.id, title: 'Pontoon Tools page action is disabled'});
            }
        } else {
            this._deactivatePageAction(tab.id);
            browser.pageAction.setTitle({tabId: tab.id, title: 'No project for this page'});
        }
    }

    /**
     * Activate the page action for tab with the right icon.
     * @param tabId to show the page action for
     * @private
     */
    _activatePageAction(tabId) {
        browser.pageAction.setIcon({
            path: {
              16: "img/pontoon-logo.svg",
              32: "img/pontoon-logo.svg"
            },
            tabId: tabId,
        });
        browser.pageAction.show(tabId);
    }

    /**
     * Show the page action for tab with the right icon.
     * @param tabId to show the page action for
     * @private
     */
    _deactivatePageAction(tabId) {
        browser.pageAction.setIcon({
            path: {
                16: "img/pontoon-logo-gray-alpha.svg",
                32: "img/pontoon-logo-gray-alpha.svg"
            },
            tabId: tabId,
        });
        browser.pageAction.hide(tabId);
    }

    /**
     * Get Pontoon project for the loaded page.
     * @param pageUrl loaded in a tab
     * @returns promise that will be fulfilled with project information or undefined, if not project is known for the url
     * @private
     */
    async _getPontoonProjectForPageUrl(pageUrl) {
        const tmpLink = document.createElement('a');
        tmpLink.href = pageUrl;
        const toProjectMap = new Map();
        const dataKey = 'projectsList';
        await browser.storage.local.get(dataKey).then((item) => {
            if (item[dataKey]) {
                Object.values(item[dataKey]).forEach((project) =>
                    project.domains.forEach((domain) => toProjectMap.set(domain, project))
                );
            }
        });
        return toProjectMap.get(tmpLink.hostname);
    }
}<|endoftext|>


<commit_before>import {Store} from 'mesosphere-shared-reactjs';

import {
  SERVER_ACTION,
  REQUEST_HEALTH_UNITS_SUCCESS,
  REQUEST_HEALTH_UNITS_ERROR,
  REQUEST_HEALTH_UNIT_SUCCESS,
  REQUEST_HEALTH_UNIT_ERROR,
  REQUEST_HEALTH_UNIT_NODES_SUCCESS,
  REQUEST_HEALTH_UNIT_NODES_ERROR,
  REQUEST_HEALTH_UNIT_NODE_SUCCESS,
  REQUEST_HEALTH_UNIT_NODE_ERROR
} from '../constants/ActionTypes';
import {
  HEALTH_UNIT_SUCCESS,
  HEALTH_UNIT_ERROR,
  HEALTH_UNIT_NODES_SUCCESS,
  HEALTH_UNIT_NODES_ERROR,
  HEALTH_UNIT_NODE_SUCCESS,
  HEALTH_UNIT_NODE_ERROR,
  HEALTH_UNITS_ERROR,
  HEALTH_UNITS_CHANGE,
  VISIBILITY_CHANGE
} from '../constants/EventTypes';
import AppDispatcher from '../events/AppDispatcher';
import Config from '../config/Config';
import UnitHealthActions from '../events/UnitHealthActions';
import HealthUnit from '../structs/HealthUnit';
import HealthUnitsList from '../structs/HealthUnitsList';
import GetSetMixin from '../mixins/GetSetMixin';
import Node from '../structs/Node';
import NodesList from '../structs/NodesList';
import VisibilityStore from './VisibilityStore';

let requestInterval = null;

function startPolling() {
  if (requestInterval == null) {
    UnitHealthActions.fetchUnits();
    requestInterval = setInterval(
      UnitHealthActions.fetchUnits, Config.getRefreshRate()
    );
  }
}

function stopPolling() {
  if (requestInterval != null) {
    clearInterval(requestInterval);
    requestInterval = null;
  }
}

function handleInactiveChange() {
  let isInactive = VisibilityStore.get('isInactive');
  if (isInactive) {
    stopPolling();
  }

  if (!isInactive && UnitHealthStore.shouldPoll()) {
    startPolling();
  }
}

VisibilityStore.addChangeListener(VISIBILITY_CHANGE, handleInactiveChange);

const UnitHealthStore = Store.createStore({

  storeID: 'unitHealth',

  mixins: [GetSetMixin],

  getSet_data: {
    units: [],
    unitsByID: {},
    nodesByUnitID: {},
    nodesByID: {}
  },

  addChangeListener: function (eventName, callback) {
    this.on(eventName, callback);

    if (this.shouldPoll()) {
      startPolling();
    }
  },

  removeChangeListener: function (eventName, callback) {
    this.removeListener(eventName, callback);

    if (!this.shouldPoll()) {
      stopPolling();
    }
  },

  shouldPoll: function () {
    return !(this.listeners(HEALTH_UNITS_CHANGE).length === 0);
  },

  getUnits: function () {
    return new HealthUnitsList({
      items: this.get('units')
    });
  },

  getUnit: function (id) {
    return new HealthUnit(this.get('unitsByID')[id] || {});
  },

  getNodes: function (unitID) {
    let nodes = this.get('nodesByUnitID')[unitID] || [];
    return new NodesList({items: nodes});
  },

  getNode: function (nodeID) {
    return new Node(this.get('nodesByID')[nodeID] || []);
  },

  getDownloadURL: function () {
    return `${Config.rootUrl}${Config.unitHealthAPIPrefix}\/report`;
  },

  fetchUnits: UnitHealthActions.fetchUnits,

  fetchUnit: UnitHealthActions.fetchUnit,

  fetchUnitNodes: UnitHealthActions.fetchUnitNodes,

  fetchUnitNode: UnitHealthActions.fetchUnitNode,

  processUnits: function (units) {
    this.set({units});

    this.emit(HEALTH_UNITS_CHANGE);
  },

  processUnit: function (unitData, unitID) {
    let unitsByID = this.get('unitsByID');
    unitsByID[unitID] = unitData;

    this.set({unitsByID});

    this.emit(HEALTH_UNIT_SUCCESS, unitID);
  },

  processNodes: function (nodes, unitID) {
    let nodesByUnitID = this.get('nodesByUnitID');
    nodesByUnitID[unitID] = nodes;

    this.set({nodesByUnitID});

    this.emit(HEALTH_UNIT_NODES_SUCCESS, unitID);
  },

  processNode: function (nodeData, unitID, nodeID) {
    let nodesByID = this.get('nodesByID');
    nodesByID[nodeID] = nodeData;

    this.set({nodesByID});

    this.emit(HEALTH_UNIT_NODE_SUCCESS, unitID, nodeID);
  },

  dispatcherIndex: AppDispatcher.register(function (payload) {
    if (payload.source !== SERVER_ACTION) {
      return false;
    }

    let action = payload.action;
    let data = action.data;

    switch (action.type) {
      case REQUEST_HEALTH_UNITS_SUCCESS:
        UnitHealthStore.processUnits(data);
        break;
      case REQUEST_HEALTH_UNITS_ERROR:
        UnitHealthStore.emit(HEALTH_UNITS_ERROR, data);
        break;
      case REQUEST_HEALTH_UNIT_SUCCESS:
        UnitHealthStore.processUnit(data, action.unitID);
        break;
      case REQUEST_HEALTH_UNIT_ERROR:
        UnitHealthStore.emit(HEALTH_UNIT_ERROR, data, action.unitID);
        break;
      case REQUEST_HEALTH_UNIT_NODES_SUCCESS:
        UnitHealthStore.processNodes(data, action.unitID);
        break;
      case REQUEST_HEALTH_UNIT_NODES_ERROR:
        UnitHealthStore.emit(HEALTH_UNIT_NODES_ERROR, data, action.unitID);
        break;
      case REQUEST_HEALTH_UNIT_NODE_SUCCESS:
        UnitHealthStore.processNode(data, action.unitID, action.nodeID);
        break;
      case REQUEST_HEALTH_UNIT_NODE_ERROR:
        UnitHealthStore.emit(HEALTH_UNIT_NODE_ERROR, data, action.unitID, action.nodeID);
        break;
    }

    return true;
  })

});

module.exports = UnitHealthStore;
<commit_msg>Update UnitHealthStore to es6 class<commit_after>import BaseStore from './BaseStore';
import {
  SERVER_ACTION,
  REQUEST_HEALTH_UNITS_SUCCESS,
  REQUEST_HEALTH_UNITS_ERROR,
  REQUEST_HEALTH_UNIT_SUCCESS,
  REQUEST_HEALTH_UNIT_ERROR,
  REQUEST_HEALTH_UNIT_NODES_SUCCESS,
  REQUEST_HEALTH_UNIT_NODES_ERROR,
  REQUEST_HEALTH_UNIT_NODE_SUCCESS,
  REQUEST_HEALTH_UNIT_NODE_ERROR
} from '../constants/ActionTypes';
import {
  HEALTH_UNIT_SUCCESS,
  HEALTH_UNIT_ERROR,
  HEALTH_UNIT_NODES_SUCCESS,
  HEALTH_UNIT_NODES_ERROR,
  HEALTH_UNIT_NODE_SUCCESS,
  HEALTH_UNIT_NODE_ERROR,
  HEALTH_UNITS_ERROR,
  HEALTH_UNITS_CHANGE,
  VISIBILITY_CHANGE
} from '../constants/EventTypes';
import AppDispatcher from '../events/AppDispatcher';
import Config from '../config/Config';
import UnitHealthActions from '../events/UnitHealthActions';
import HealthUnit from '../structs/HealthUnit';
import HealthUnitsList from '../structs/HealthUnitsList';
import Node from '../structs/Node';
import NodesList from '../structs/NodesList';
import VisibilityStore from './VisibilityStore';

let requestInterval = null;

function startPolling() {
  if (requestInterval == null) {
    UnitHealthActions.fetchUnits();
    requestInterval = setInterval(
      UnitHealthActions.fetchUnits, Config.getRefreshRate()
    );
  }
}

function stopPolling() {
  if (requestInterval != null) {
    clearInterval(requestInterval);
    requestInterval = null;
  }
}

class UnitHealthStore extends BaseStore {
  constructor() {
    super(...arguments);

    this.getSet_data = {
      units: [],
      unitsByID: {},
      nodesByUnitID: {},
      nodesByID: {}
    };

    this.dispatcherIndex = AppDispatcher.register((payload) => {
      if (payload.source !== SERVER_ACTION) {
        return false;
      }

      let action = payload.action;
      let data = action.data;

      switch (action.type) {
        case REQUEST_HEALTH_UNITS_SUCCESS:
          this.processUnits(data);
          break;
        case REQUEST_HEALTH_UNITS_ERROR:
          this.emit(HEALTH_UNITS_ERROR, data);
          break;
        case REQUEST_HEALTH_UNIT_SUCCESS:
          this.processUnit(data, action.unitID);
          break;
        case REQUEST_HEALTH_UNIT_ERROR:
          this.emit(HEALTH_UNIT_ERROR, data, action.unitID);
          break;
        case REQUEST_HEALTH_UNIT_NODES_SUCCESS:
          this.processNodes(data, action.unitID);
          break;
        case REQUEST_HEALTH_UNIT_NODES_ERROR:
          this.emit(HEALTH_UNIT_NODES_ERROR, data, action.unitID);
          break;
        case REQUEST_HEALTH_UNIT_NODE_SUCCESS:
          this.processNode(data, action.unitID, action.nodeID);
          break;
        case REQUEST_HEALTH_UNIT_NODE_ERROR:
          this.emit(HEALTH_UNIT_NODE_ERROR, data, action.unitID, action.nodeID);
          break;
      }

      return true;
    });

    VisibilityStore.addChangeListener(
      VISIBILITY_CHANGE,
      this.onVisibilityStoreChange.bind(this)
    );
  }

  addChangeListener(eventName, callback) {
    this.on(eventName, callback);

    if (this.shouldPoll()) {
      startPolling();
    }
  }

  removeChangeListener(eventName, callback) {
    this.removeListener(eventName, callback);

    if (!this.shouldPoll()) {
      stopPolling();
    }
  }

  onVisibilityStoreChange() {
    if (!VisibilityStore.isInactive() && this.shouldPoll()) {
      startPolling();
      return;
    }

    stopPolling();
  }

  shouldPoll() {
    return !(this.listeners(HEALTH_UNITS_CHANGE).length === 0);
  }

  getUnits() {
    return new HealthUnitsList({
      items: this.get('units')
    });
  }

  getUnit(id) {
    return new HealthUnit(this.get('unitsByID')[id] || {});
  }

  getNodes(unitID) {
    let nodes = this.get('nodesByUnitID')[unitID] || [];
    return new NodesList({items: nodes});
  }

  getNode(nodeID) {
    return new Node(this.get('nodesByID')[nodeID] || []);
  }

  getDownloadURL() {
    return `${Config.rootUrl}${Config.unitHealthAPIPrefix}\/report`;
  }

  fetchUnits() {
    return UnitHealthActions.fetchUnits(...arguments);
  }

  fetchUnit() {
    return UnitHealthActions.fetchUnit(...arguments);
  }

  fetchUnitNodes() {
    return UnitHealthActions.fetchUnitNodes(...arguments);
  }

  fetchUnitNode() {
    return UnitHealthActions.fetchUnitNode(...arguments);
  }

  processUnits(units) {
    this.set({units});

    this.emit(HEALTH_UNITS_CHANGE);
  }

  processUnit(unitData, unitID) {
    let unitsByID = this.get('unitsByID');
    unitsByID[unitID] = unitData;

    this.set({unitsByID});

    this.emit(HEALTH_UNIT_SUCCESS, unitID);
  }

  processNodes(nodes, unitID) {
    let nodesByUnitID = this.get('nodesByUnitID');
    nodesByUnitID[unitID] = nodes;

    this.set({nodesByUnitID});

    this.emit(HEALTH_UNIT_NODES_SUCCESS, unitID);
  }

  processNode(nodeData, unitID, nodeID) {
    let nodesByID = this.get('nodesByID');
    nodesByID[nodeID] = nodeData;

    this.set({nodesByID});

    this.emit(HEALTH_UNIT_NODE_SUCCESS, unitID, nodeID);
  }

  get storeID() {
    return 'unitHealth';
  }

}

module.exports = new UnitHealthStore();
<|endoftext|>