<commit_before>QUnit.module("Loader Factory", {
    before: function() {
        console.log("Kickoff: " + Math.random());
    },
    beforeEach: function() {
        // console.log("Creating new test object");
        this.image_loader = new ImageLoader_0_5_x();
    }
});

/************************************************************\
 * Set Defaults and Version Number
\************************************************************/

QUnit.test("Confirm version number", function(assert) {
    var target_version = "0.5.2";
    assert.equal(target_version, this.image_loader.version_number());
});

///

QUnit.test("Confirm default for .alt_text()", function(assert) {
    var target = "";
    var result = this.image_loader.alt_text();
    assert.equal(result, target);
});

QUnit.test("Confirm default for .dpr()", function(assert) {
    var target = 0;
    var result = this.image_loader.dpr();
    assert.equal(result, target);
});

QUnit.test("Confirm default for .filename()", function(assert) {
    var target = "";
    var result = this.image_loader.filename();
    assert.equal(result, target);
});

QUnit.test("Confirm default for .img_tag_template()", function(assert) {
    var target = '';
    var result = this.image_loader.img_tag_template();
    assert.equal(result, target);
});

QUnit.test("Confirm default for .max_percent_of_viewport_logical_width()", function(assert) {
    var target = 94;
    var result = this.image_loader.max_percent_of_viewport_logical_width();
    assert.equal(result, target);
});

QUnit.test("Confirm default for .raw_source_heigth()", function(assert) {
    var target = 0;
    var result = this.image_loader.raw_source_physical_height();
    assert.equal(result, target);
});

QUnit.test("Confirm default for .raw_source_physical_width()", function(assert) {
    var target = 0;
    var result = this.image_loader.raw_source_physical_width();
    assert.equal(result, target);
});

QUnit.test("Confirm default for .url_template()", function(assert) {
    var target = "";
    var result = this.image_loader.url_template();
    assert.equal(result, target);
});

QUnit.test("Confirm default for .viewport_logical_height()", function(assert) {
    var target = 0;
    var result = this.image_loader.viewport_logical_height();
    assert.equal(result, target);
});

QUnit.test("Confirm default for .viewport_logical_width()", function(assert) {
    var target = 0;
    var result = this.image_loader.viewport_logical_width();
    assert.equal(result, target);
});

/************************************************************\
 * Integration Tests
\************************************************************/

QUnit.test ("Integration Test 1: Base functionality with minimal call", function(assert) {
    // Remember: There should be no need to test different iterations of limiting
    // either by physical dimension or viewport percentage. All that math should be
    // tested at the unit level. As long as a single pass through here works for
    // the minimum set of values, the unit tests are responsible for handling
    // everything else. (It may be worth setting up a run thru multiple just
    // as an exercise, but for dev, just use it and let that experience determine
    // if you need to add more and if so where.)
    //
    // There should also be no need to do another version with a custom setting
    // for `max_percetage_of_viewport_logical_width`. That is already being
    // exercies here with a value of `94` and setting a custom value would
    // just push the new value through the exact same logic/formula.

    // Preflight
    var target = '';

    // Force environmental variables for testing consistency
    // NOTE: the normal funcation isn't used because these values would be dynamic
    this.image_loader._dpr = 1.6;
    this.image_loader._viewport_logical_width = 1024;
    this.image_loader._viewport_logical_height = 680;

    // Given
    this.image_loader._url_template = '//res.cloudinary.com/demo/image/upload/w_[PHYSICAL_WIDTH_TO_CALL],h_[PHYSICAL_HEIGHT_TO_CALL]/[FILENAME]';

    // When
    this.image_loader._alt_text = "Photo of Horses";
    this.image_loader._filename = "horses.jpg";
    this.image_loader._raw_source_physical_width = 1600;
    this.image_loader._raw_source_physical_height = 1067;

    var result = this.image_loader.img_tag_string();

    // Then
    assert.equal(result, target);

});

/************************************************************\
 * Unit Tests
\************************************************************/

QUnit.test("Unit Test: .load_environment_with_url_template(STRING)", function(assert) {
    // NOTE: This function loads dynamic variables from the environment.
    // Confirmation is done by comparing the same env variables pulled
    // dynamically during testing execution. Seems like the simpelest way
    // to verify things are loaded. Not sure what would happen if testing
    // is done in a headless environment. Will cross that bridge if it
    // becomes necessary.

    // Given
    this.image_loader.load_environment_with_url_template(
        '//res.cloudinary.com/demo/image/upload/w_[PHYSICAL_WIDTH_TO_CALL],h_[PHYSICAL_HEIGHT_TO_CALL]/[FILENAME]'
    );

    // Then
    assert.equal(this.image_loader.viewport_logical_width(), window.innerWidth, "Viewport Width");
    assert.equal(this.image_loader.viewport_logical_height(), window.innerHeight, "Viewport Height");
    assert.equal(this.image_loader.dpr(), window.devicePixelRatio, "Device Pixel Ratio");

});

QUnit.test("Unit Test: .logical_height()", function(assert) {
    // Preflight
    var target = 340;

    // Given
    this.image_loader._dpr = 2;
    this.image_loader._max_percent_of_viewport_logical_width = 50;
    this.image_loader._raw_source_physical_height = 1067;
    this.image_loader._raw_source_physical_width = 1600;
    this.image_loader._viewport_logical_width = 1024;

    // When
    var result = this.image_loader.logical_height();

    // Then
    assert.equal(result, target);
});

QUnit.test("Unit Test: .logical_width() - when .viewport_percentage_max_logical_width() is returned", function(assert) {

    // NOTE: Without rounding, the value would be `527`, but it's rounded down to `520`.

    // Preflight
    var target = 520;

    // Given
    this.image_loader._dpr = 2;
    this.image_loader._max_percent_of_viewport_logical_width = 51;
    this.image_loader._raw_source_physical_height = 1067;
    this.image_loader._raw_source_physical_width = 1600;
    this.image_loader._viewport_logical_width = 1035;

    // When
    var result = this.image_loader.logical_width();

    // Then
    assert.equal(result, target);
});

QUnit.test("Unit Test: .logical_width() - when .raw_source_dpr_max_logical_width() is returned", function(assert) {
    // Preflight
    var target = 400;

    // Given
    this.image_loader._dpr = 2;
    this.image_loader._max_percent_of_viewport_logical_width = 100;
    this.image_loader._raw_source_physical_height = 600;
    this.image_loader._raw_source_physical_width = 800;
    this.image_loader._viewport_logical_width = 1024;

    // When
    var result = this.image_loader.logical_width();

    // Then
    assert.equal(result, target);
});

QUnit.test("Unit Test: .physical_height_to_call()", function(assert) {
    // Preflight
    var target = 600;

    // Given
    this.image_loader._dpr = 2;
    this.image_loader._max_percent_of_viewport_logical_width = 100;
    this.image_loader._raw_source_physical_height = 600;
    this.image_loader._raw_source_physical_width = 800;
    this.image_loader._viewport_logical_width = 1024;

    // When
    var result = this.image_loader.physical_height_to_call();

    // Then
    assert.equal(result, target);
});

QUnit.test("Unit Test: .physical_width_to_call()", function(assert) {
    // NOTE: This test doesn't exercise the `Math.floor` portion
    // of the function. I'm not sure if it's necessary for the funciton,
    // but it's been added for safety. TODO: look into this to see
    // if it's necessary and if so, design a test to verify it.

    // Preflight
    var target = 800;

    // Given
    this.image_loader._dpr = 2;
    this.image_loader._max_percent_of_viewport_logical_width = 100;
    this.image_loader._raw_source_physical_height = 600;
    this.image_loader._raw_source_physical_width = 800;
    this.image_loader._viewport_logical_width = 1024;

    // When
    var result = this.image_loader.physical_width_to_call();

    // Then
    assert.equal(result, target);
});

QUnit.test("Unit Test: .raw_source_dpr_max_logical_width()", function(assert) {
    // Preflight
    var target = 87;

    // Given
    this.image_loader._dpr = 2;
    this.image_loader._raw_source_physical_width = 175;

    // When
    var result = this.image_loader.raw_source_dpr_max_logical_width();

    // Then
    assert.equal(result, target);
});

QUnit.test("Unit Test: .url_string()", function(assert) {
    // Preflight
    var target = '//res.cloudinary.com/demo/image/upload/c_fill,w_1020,h_680/horses.jpg';

    // Given
    // this.image_loader._alt_text = "Photo of Horses";
    this.image_loader._filename = "horses.jpg";
    this.image_loader._max_percent_of_viewport_logical_width = 50;
    this.image_loader._raw_source_physical_height = 1067;
    this.image_loader._raw_source_physical_width = 1600;
    this.image_loader._url_template = '//res.cloudinary.com/demo/image/upload/c_fill,w_[PHYSICAL_WIDTH_TO_CALL],h_[PHYSICAL_HEIGHT_TO_CALL]/[FILENAME]';

    // Force environmental variables for testing consistency
    this.image_loader._dpr = 2;
    this.image_loader._viewport_logical_height = 680;
    this.image_loader._viewport_logical_width = 1024;

    // When
    var result = this.image_loader.url_string();

    // Then
    assert.equal(result, target);

});

QUnit.test("Unit Test: .viewport_percentage_max_logical_width()", function(assert) {
    // Preflight
    var target = 819;

    // Given
    this.image_loader._max_percent_of_viewport_logical_width = 80;
    this.image_loader._viewport_logical_width = 1024;

    // When
    var result = this.image_loader.viewport_percentage_max_logical_width();

    // Then
    assert.equal(result, target);
});

<commit_msg>Added `c_fill` to integration test so the example is consistent with what should be used in production.<commit_after>QUnit.module("Loader Factory", {
    before: function() {
        console.log("Kickoff: " + Math.random());
    },
    beforeEach: function() {
        // console.log("Creating new test object");
        this.image_loader = new ImageLoader_0_5_x();
    }
});

/************************************************************\
 * Set Defaults and Version Number
\************************************************************/

QUnit.test("Confirm version number", function(assert) {
    var target_version = "0.5.2";
    assert.equal(target_version, this.image_loader.version_number());
});

///

QUnit.test("Confirm default for .alt_text()", function(assert) {
    var target = "";
    var result = this.image_loader.alt_text();
    assert.equal(result, target);
});

QUnit.test("Confirm default for .dpr()", function(assert) {
    var target = 0;
    var result = this.image_loader.dpr();
    assert.equal(result, target);
});

QUnit.test("Confirm default for .filename()", function(assert) {
    var target = "";
    var result = this.image_loader.filename();
    assert.equal(result, target);
});

QUnit.test("Confirm default for .img_tag_template()", function(assert) {
    var target = '';
    var result = this.image_loader.img_tag_template();
    assert.equal(result, target);
});

QUnit.test("Confirm default for .max_percent_of_viewport_logical_width()", function(assert) {
    var target = 94;
    var result = this.image_loader.max_percent_of_viewport_logical_width();
    assert.equal(result, target);
});

QUnit.test("Confirm default for .raw_source_heigth()", function(assert) {
    var target = 0;
    var result = this.image_loader.raw_source_physical_height();
    assert.equal(result, target);
});

QUnit.test("Confirm default for .raw_source_physical_width()", function(assert) {
    var target = 0;
    var result = this.image_loader.raw_source_physical_width();
    assert.equal(result, target);
});

QUnit.test("Confirm default for .url_template()", function(assert) {
    var target = "";
    var result = this.image_loader.url_template();
    assert.equal(result, target);
});

QUnit.test("Confirm default for .viewport_logical_height()", function(assert) {
    var target = 0;
    var result = this.image_loader.viewport_logical_height();
    assert.equal(result, target);
});

QUnit.test("Confirm default for .viewport_logical_width()", function(assert) {
    var target = 0;
    var result = this.image_loader.viewport_logical_width();
    assert.equal(result, target);
});

/************************************************************\
 * Integration Tests
\************************************************************/

QUnit.test ("Integration Test 1: Base functionality with minimal call", function(assert) {
    // Remember: There should be no need to test different iterations of limiting
    // either by physical dimension or viewport percentage. All that math should be
    // tested at the unit level. As long as a single pass through here works for
    // the minimum set of values, the unit tests are responsible for handling
    // everything else. (It may be worth setting up a run thru multiple just
    // as an exercise, but for dev, just use it and let that experience determine
    // if you need to add more and if so where.)
    //
    // There should also be no need to do another version with a custom setting
    // for `max_percetage_of_viewport_logical_width`. That is already being
    // exercies here with a value of `94` and setting a custom value would
    // just push the new value through the exact same logic/formula.

    // Preflight
    var target = '';

    // Force environmental variables for testing consistency
    // NOTE: the normal funcation isn't used because these values would be dynamic
    this.image_loader._dpr = 1.6;
    this.image_loader._viewport_logical_width = 1024;
    this.image_loader._viewport_logical_height = 680;

    // Given
    this.image_loader._url_template = '//res.cloudinary.com/demo/image/upload/w_[PHYSICAL_WIDTH_TO_CALL],h_[PHYSICAL_HEIGHT_TO_CALL]/[FILENAME]';

    // When
    this.image_loader._alt_text = "Photo of Horses";
    this.image_loader._filename = "horses.jpg";
    this.image_loader._raw_source_physical_width = 1600;
    this.image_loader._raw_source_physical_height = 1067;

    var result = this.image_loader.img_tag_string();

    // Then
    assert.equal(result, target);

});

/************************************************************\
 * Unit Tests
\************************************************************/

QUnit.test("Unit Test: .load_environment_with_url_template(STRING)", function(assert) {
    // NOTE: This function loads dynamic variables from the environment.
    // Confirmation is done by comparing the same env variables pulled
    // dynamically during testing execution. Seems like the simpelest way
    // to verify things are loaded. Not sure what would happen if testing
    // is done in a headless environment. Will cross that bridge if it
    // becomes necessary.

    // Given
    this.image_loader.load_environment_with_url_template(
        '//res.cloudinary.com/demo/image/upload/w_[PHYSICAL_WIDTH_TO_CALL],h_[PHYSICAL_HEIGHT_TO_CALL]/[FILENAME]'
    );

    // Then
    assert.equal(this.image_loader.viewport_logical_width(), window.innerWidth, "Viewport Width");
    assert.equal(this.image_loader.viewport_logical_height(), window.innerHeight, "Viewport Height");
    assert.equal(this.image_loader.dpr(), window.devicePixelRatio, "Device Pixel Ratio");

});

QUnit.test("Unit Test: .logical_height()", function(assert) {
    // Preflight
    var target = 340;

    // Given
    this.image_loader._dpr = 2;
    this.image_loader._max_percent_of_viewport_logical_width = 50;
    this.image_loader._raw_source_physical_height = 1067;
    this.image_loader._raw_source_physical_width = 1600;
    this.image_loader._viewport_logical_width = 1024;

    // When
    var result = this.image_loader.logical_height();

    // Then
    assert.equal(result, target);
});

QUnit.test("Unit Test: .logical_width() - when .viewport_percentage_max_logical_width() is returned", function(assert) {

    // NOTE: Without rounding, the value would be `527`, but it's rounded down to `520`.

    // Preflight
    var target = 520;

    // Given
    this.image_loader._dpr = 2;
    this.image_loader._max_percent_of_viewport_logical_width = 51;
    this.image_loader._raw_source_physical_height = 1067;
    this.image_loader._raw_source_physical_width = 1600;
    this.image_loader._viewport_logical_width = 1035;

    // When
    var result = this.image_loader.logical_width();

    // Then
    assert.equal(result, target);
});

QUnit.test("Unit Test: .logical_width() - when .raw_source_dpr_max_logical_width() is returned", function(assert) {
    // Preflight
    var target = 400;

    // Given
    this.image_loader._dpr = 2;
    this.image_loader._max_percent_of_viewport_logical_width = 100;
    this.image_loader._raw_source_physical_height = 600;
    this.image_loader._raw_source_physical_width = 800;
    this.image_loader._viewport_logical_width = 1024;

    // When
    var result = this.image_loader.logical_width();

    // Then
    assert.equal(result, target);
});

QUnit.test("Unit Test: .physical_height_to_call()", function(assert) {
    // Preflight
    var target = 600;

    // Given
    this.image_loader._dpr = 2;
    this.image_loader._max_percent_of_viewport_logical_width = 100;
    this.image_loader._raw_source_physical_height = 600;
    this.image_loader._raw_source_physical_width = 800;
    this.image_loader._viewport_logical_width = 1024;

    // When
    var result = this.image_loader.physical_height_to_call();

    // Then
    assert.equal(result, target);
});

QUnit.test("Unit Test: .physical_width_to_call()", function(assert) {
    // NOTE: This test doesn't exercise the `Math.floor` portion
    // of the function. I'm not sure if it's necessary for the funciton,
    // but it's been added for safety. TODO: look into this to see
    // if it's necessary and if so, design a test to verify it.

    // Preflight
    var target = 800;

    // Given
    this.image_loader._dpr = 2;
    this.image_loader._max_percent_of_viewport_logical_width = 100;
    this.image_loader._raw_source_physical_height = 600;
    this.image_loader._raw_source_physical_width = 800;
    this.image_loader._viewport_logical_width = 1024;

    // When
    var result = this.image_loader.physical_width_to_call();

    // Then
    assert.equal(result, target);
});

QUnit.test("Unit Test: .raw_source_dpr_max_logical_width()", function(assert) {
    // Preflight
    var target = 87;

    // Given
    this.image_loader._dpr = 2;
    this.image_loader._raw_source_physical_width = 175;

    // When
    var result = this.image_loader.raw_source_dpr_max_logical_width();

    // Then
    assert.equal(result, target);
});

QUnit.test("Unit Test: .url_string()", function(assert) {
    // Preflight
    var target = '//res.cloudinary.com/demo/image/upload/c_fill,w_1020,h_680/horses.jpg';

    // Given
    // this.image_loader._alt_text = "Photo of Horses";
    this.image_loader._filename = "horses.jpg";
    this.image_loader._max_percent_of_viewport_logical_width = 50;
    this.image_loader._raw_source_physical_height = 1067;
    this.image_loader._raw_source_physical_width = 1600;
    this.image_loader._url_template = '//res.cloudinary.com/demo/image/upload/c_fill,w_[PHYSICAL_WIDTH_TO_CALL],h_[PHYSICAL_HEIGHT_TO_CALL]/[FILENAME]';

    // Force environmental variables for testing consistency
    this.image_loader._dpr = 2;
    this.image_loader._viewport_logical_height = 680;
    this.image_loader._viewport_logical_width = 1024;

    // When
    var result = this.image_loader.url_string();

    // Then
    assert.equal(result, target);

});

QUnit.test("Unit Test: .viewport_percentage_max_logical_width()", function(assert) {
    // Preflight
    var target = 819;

    // Given
    this.image_loader._max_percent_of_viewport_logical_width = 80;
    this.image_loader._viewport_logical_width = 1024;

    // When
    var result = this.image_loader.viewport_percentage_max_logical_width();

    // Then
    assert.equal(result, target);
});

<|endoftext|>


<commit_before>QUnit.module("find coalescing");

QUnit.test("multiple calls to Model#find within the same run loop coalesce into a findMany call", function(assert) {
  assert.expect(2);

  var Model = Ember.Model.extend();

  Model.adapter = {
    find: function() {
      assert.ok(false, "find was called");
    },

    findMany: function(klass, records, ids) {
      assert.ok(true, "findMany was called");
      assert.deepEqual(ids, [1,2,3], "The correct ids were passed into findMany");
    }
  };

  Ember.run(function() {
    Model.find(1);
    Model.find(2);
    Model.find(3);
  });
});

QUnit.test("coalesced findMany call should only include records which aren't loaded in the identity map", function(assert) {
  assert.expect(2);

  var Model = Ember.Model.extend({
    id: Ember.attr()
  });

  Model.adapter = {
    findMany: function(klass, records, ids) {
      assert.ok(true, "findMany was called");
      assert.deepEqual(ids, [2,3], "The correct ids were passed into findMany");
    }
  };

  var record = Model.create({ id: 1 });
  Ember.run(record, record.didCreateRecord);
  Ember.run(record, record.load, 1);

  Ember.run(function() {
    Model.find([1, 2, 3]);
  });
});

QUnit.test("coalesced findMany returns a resolved promise even if all records are loaded from cache", function(assert) {
  assert.expect(1);

  var Model = Ember.Model.extend({
    id: Ember.attr()
  });

  Model.adapter = {
    findMany: function(klass, records, ids) {
      assert.ok(false, "findMany shouldn't be called");
    }
  };

  var record = Model.create({ id: 1 });
  Ember.run(record, record.didCreateRecord);
  Ember.run(record, record.load, 1);

  var record2 = Model.create({ id: 2 });
  Ember.run(record2, record.didCreateRecord);
  Ember.run(record2, record.load, 2);

  var promise = Ember.run(Model, Model.fetch, [1, 2]);

  Ember.run(function() {
    promise.then(function(records) {
      assert.equal(records.get("length"), 2);
    });
  });
});

QUnit.test("calls to Model#find and Model#findMany within the same run loop coalesce into a single findMany call", function(assert) {
  assert.expect(2);

  var Model = Ember.Model.extend();

  Model.adapter = {
    find: function() {
      assert.ok(false, "find was called");
    },

    findMany: function(klass, records, ids) {
      assert.ok(true, "findMany was called");
      assert.deepEqual(ids, [1,2,3], "The correct ids were passed into findMany");
    }
  };

  Ember.run(function() {
    Model.find(1);
    Model.find([2, 3]);
  });
});

QUnit.test("should unique IDs", function(assert) {
  assert.expect(2);

  var Model = Ember.Model.extend();

  Model.adapter = {
    find: function() {
      assert.ok(false, "find was called");
    },

    findMany: function(klass, records, ids) {
      assert.ok(true, "findMany was called");
      assert.deepEqual(ids, [1,2,3], "The correct ids were passed into findMany");
      records.load(klass, []);
    }
  };

  Ember.run(function() {
    Model.find(1);
    Model.find(1);
    Model.find([2, 3]);
    Model.find([1, 2, 3]);
  });
});

QUnit.test("should resolve all RecordArrays", function(assert) {
  assert.expect(2);

  var Model = Ember.Model.extend();

  Model.adapter = {
    findMany: function(klass, records, ids) {
      records.load(klass, []);
    }
  };

  var promise1, promise2;

  Ember.run(function() {
    Model.find(1);
    Model.find(1);
    promise1 = Model.fetch([2, 3]);
    promise2 = Model.fetch([1, 2, 3]);

    promise1.then(function() {
      assert.ok(true, "The first RecordArray returned from findMany was loaded");
    });

    promise2.then(function() {
      assert.ok(true, "The second RecordArray returned from findMany was loaded");
    });
  });
});
<commit_msg>Fix find_coalescing tests<commit_after>QUnit.module("find coalescing");

QUnit.test("multiple calls to Model#find within the same run loop coalesce into a findMany call", function(assert) {
  assert.expect(2);

  var Model = Ember.Model.extend();

  Model.adapter = {
    find: function() {
      assert.ok(false, "find was called");
    },

    findMany: function(klass, records, ids) {
      assert.ok(true, "findMany was called");
      assert.deepEqual(ids, [1,2,3], "The correct ids were passed into findMany");
      return Ember.RSVP.resolve();
    }
  };

  Ember.run(function() {
    Model.find(1);
    Model.find(2);
    Model.find(3);
  });
});

QUnit.test("coalesced findMany call should only include records which aren't loaded in the identity map", function(assert) {
  assert.expect(2);

  var Model = Ember.Model.extend({
    id: Ember.attr()
  });

  Model.adapter = {
    findMany: function(klass, records, ids) {
      assert.ok(true, "findMany was called");
      assert.deepEqual(ids, [2,3], "The correct ids were passed into findMany");
      return Ember.RSVP.resolve();
    }
  };

  var record = Model.create({ id: 1 });
  Ember.run(record, record.didCreateRecord);
  Ember.run(record, record.load, 1);

  Ember.run(function() {
    Model.find([1, 2, 3]);
  });
});

QUnit.test("coalesced findMany returns a resolved promise even if all records are loaded from cache", function(assert) {
  assert.expect(1);

  var Model = Ember.Model.extend({
    id: Ember.attr()
  });

  Model.adapter = {
    findMany: function(klass, records, ids) {
      assert.ok(false, "findMany shouldn't be called");
    }
  };

  var record = Model.create({ id: 1 });
  Ember.run(record, record.didCreateRecord);
  Ember.run(record, record.load, 1);

  var record2 = Model.create({ id: 2 });
  Ember.run(record2, record.didCreateRecord);
  Ember.run(record2, record.load, 2);

  var promise = Ember.run(Model, Model.fetch, [1, 2]);

  Ember.run(function() {
    promise.then(function(records) {
      assert.equal(records.get("length"), 2);
    });
  });
});

QUnit.test("calls to Model#find and Model#findMany within the same run loop coalesce into a single findMany call", function(assert) {
  assert.expect(2);

  var Model = Ember.Model.extend();

  Model.adapter = {
    find: function() {
      assert.ok(false, "find was called");
    },

    findMany: function(klass, records, ids) {
      assert.ok(true, "findMany was called");
      assert.deepEqual(ids, [1,2,3], "The correct ids were passed into findMany");
      return Ember.RSVP.resolve();
    }
  };

  Ember.run(function() {
    Model.find(1);
    Model.find([2, 3]);
  });
});

QUnit.test("should unique IDs", function(assert) {
  assert.expect(2);

  var Model = Ember.Model.extend();

  Model.adapter = {
    find: function() {
      assert.ok(false, "find was called");
    },

    findMany: function(klass, records, ids) {
      assert.ok(true, "findMany was called");
      assert.deepEqual(ids, [1,2,3], "The correct ids were passed into findMany");
      records.load(klass, []);
      return Ember.RSVP.resolve();
    }
  };

  Ember.run(function() {
    Model.find(1);
    Model.find(1);
    Model.find([2, 3]);
    Model.find([1, 2, 3]);
  });
});

QUnit.test("should resolve all RecordArrays", function(assert) {
  assert.expect(2);

  var Model = Ember.Model.extend();

  Model.adapter = {
    findMany: function(klass, records, ids) {
      records.load(klass, []);
      return Ember.RSVP.resolve();
    }
  };

  var promise1, promise2;

  Ember.run(function() {
    Model.find(1);
    Model.find(1);
    promise1 = Model.fetch([2, 3]);
    promise2 = Model.fetch([1, 2, 3]);

    promise1.then(function() {
      assert.ok(true, "The first RecordArray returned from findMany was loaded");
    });

    promise2.then(function() {
      assert.ok(true, "The second RecordArray returned from findMany was loaded");
    });
  });
});
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2021 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import {AnonymousAvatar} from '@canvas/discussions/react/components/AnonymousAvatar/AnonymousAvatar'
import {CURRENT_USER, DEFAULT_AVATAR_URL} from '../../utils/constants'
import I18n from 'i18n!discussions_posts'
import React, {useState} from 'react'

import {Avatar} from '@instructure/ui-avatar'
import {Flex} from '@instructure/ui-flex'
import {Select} from '@instructure/ui-select'
import {Text} from '@instructure/ui-text'
import {View} from '@instructure/ui-view'
import propTypes from 'prop-types'

export const AnonymousResponseSelector = props => {
  const [selectedOption, setSelectedOption] = useState(
    props.discussionAnonymousState ? 'Anonymous' : props.username
  )
  const [showOptions, setShowOptions] = useState(false)
  const [highlightedOption, setHighlightedOption] = useState(null)

  const replyAsOption = () => {
    if (props.discussionAnonymousState === 'partial_anonymity') {
      return (
        <Select
          inputValue={selectedOption}
          isShowingOptions={showOptions}
          onRequestShowOptions={() => {
            setShowOptions(true)
          }}
          onRequestHideOptions={() => {
            setShowOptions(false)
          }}
          onRequestSelectOption={(event, {id}) => {
            setShowOptions(false)
            setSelectedOption(id)
            props.setAnonymousAuthorState(id === 'Anonymous')
          }}
          onBlur={() => {
            setHighlightedOption(null)
          }}
          onRequestHighlightOption={(event, {id}) => {
            setHighlightedOption(id)
          }}
          renderLabel={
            <Text size="small" weight="light">
              {I18n.t('Replying as')}
            </Text>
          }
        >
          <Select.Option
            id="Anonymous"
            key="Anonymous"
            renderBeforeLabel={<AnonymousAvatar size="small" seedString={CURRENT_USER} />}
            isHighlighted={highlightedOption === 'Anonymous'}
          >
            <Text>{I18n.t('Anonymous')}</Text>
          </Select.Option>
          <Select.Option
            id={props.username}
            key={props.username}
            isHighlighted={highlightedOption === props.username}
            renderBeforeLabel={
              <Avatar
                size="xx-small"
                src={props.avatarUrl === DEFAULT_AVATAR_URL ? null : props.avatarUrl}
                name={props.username}
                color="licorice"
                hasInverseColor
                margin="0 small 0 0"
              />
            }
          >
            <Text>{props.username}</Text>
          </Select.Option>
        </Select>
      )
    }
    return (
      <Flex direction="column">
        <Text size="small" weight="light">
          {I18n.t('Replying as')}
        </Text>
        <Text>{props.discussionAnonymousState ? 'Anonymous' : props.username}</Text>
      </Flex>
    )
  }

  return (
    <Flex data-testid="anonymous-response-selector">
      {selectedOption === 'Anonymous' ? (
        <AnonymousAvatar seedString={CURRENT_USER} />
      ) : (
        <Avatar
          name={props.username}
          src={props.avatarUrl === DEFAULT_AVATAR_URL ? null : props.avatarUrl}
          color="licorice"
          hasInverseColor
        />
      )}
      <View margin="0 0 0 small">{replyAsOption()}</View>
    </Flex>
  )
}

AnonymousResponseSelector.propTypes = {
  avatarUrl: propTypes.string,
  username: propTypes.string,
  setAnonymousAuthorState: propTypes.func,
  discussionAnonymousState: propTypes.string
}

export default AnonymousResponseSelector
<commit_msg>Fix focus cut of on anonymous response selector<commit_after>/*
 * Copyright (C) 2021 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import {AnonymousAvatar} from '@canvas/discussions/react/components/AnonymousAvatar/AnonymousAvatar'
import {CURRENT_USER, DEFAULT_AVATAR_URL} from '../../utils/constants'
import I18n from 'i18n!discussions_posts'
import React, {useState} from 'react'

import {Avatar} from '@instructure/ui-avatar'
import {Flex} from '@instructure/ui-flex'
import {Select} from '@instructure/ui-select'
import {Text} from '@instructure/ui-text'
import {View} from '@instructure/ui-view'
import propTypes from 'prop-types'

export const AnonymousResponseSelector = props => {
  const [selectedOption, setSelectedOption] = useState(
    props.discussionAnonymousState ? 'Anonymous' : props.username
  )
  const [showOptions, setShowOptions] = useState(false)
  const [highlightedOption, setHighlightedOption] = useState(null)

  const replyAsOption = () => {
    if (props.discussionAnonymousState === 'partial_anonymity') {
      return (
        <Select
          inputValue={selectedOption}
          isShowingOptions={showOptions}
          onRequestShowOptions={() => {
            setShowOptions(true)
          }}
          onRequestHideOptions={() => {
            setShowOptions(false)
          }}
          onRequestSelectOption={(event, {id}) => {
            setShowOptions(false)
            setSelectedOption(id)
            props.setAnonymousAuthorState(id === 'Anonymous')
          }}
          onBlur={() => {
            setHighlightedOption(null)
          }}
          onRequestHighlightOption={(event, {id}) => {
            setHighlightedOption(id)
          }}
          renderLabel={
            <Text size="small" weight="light">
              {I18n.t('Replying as')}
            </Text>
          }
        >
          <Select.Option
            id="Anonymous"
            key="Anonymous"
            renderBeforeLabel={<AnonymousAvatar size="small" seedString={CURRENT_USER} />}
            isHighlighted={highlightedOption === 'Anonymous'}
          >
            <Text>{I18n.t('Anonymous')}</Text>
          </Select.Option>
          <Select.Option
            id={props.username}
            key={props.username}
            isHighlighted={highlightedOption === props.username}
            renderBeforeLabel={
              <Avatar
                size="xx-small"
                src={props.avatarUrl === DEFAULT_AVATAR_URL ? null : props.avatarUrl}
                name={props.username}
                color="licorice"
                hasInverseColor
                margin="0 small 0 0"
              />
            }
          >
            <Text>{props.username}</Text>
          </Select.Option>
        </Select>
      )
    }
    return (
      <Flex direction="column">
        <Text size="small" weight="light">
          {I18n.t('Replying as')}
        </Text>
        <Text>{props.discussionAnonymousState ? 'Anonymous' : props.username}</Text>
      </Flex>
    )
  }

  return (
    <Flex data-testid="anonymous-response-selector">
      {selectedOption === 'Anonymous' ? (
        <AnonymousAvatar seedString={CURRENT_USER} />
      ) : (
        <Avatar
          name={props.username}
          src={props.avatarUrl === DEFAULT_AVATAR_URL ? null : props.avatarUrl}
          color="licorice"
          hasInverseColor
        />
      )}
      <View margin="0 0 small small">{replyAsOption()}</View>
    </Flex>
  )
}

AnonymousResponseSelector.propTypes = {
  avatarUrl: propTypes.string,
  username: propTypes.string,
  setAnonymousAuthorState: propTypes.func,
  discussionAnonymousState: propTypes.string
}

export default AnonymousResponseSelector
<|endoftext|>


<commit_before>/**
@module ember
@submodule ember-runtime
*/

const OUT_OF_RANGE_EXCEPTION = 'Index out of range';
const EMPTY = [];

// ..........................................................
// HELPERS
//

import {
  get,
  Error as EmberError,
  Mixin
} from 'ember-metal';
import EmberArray, { objectAt } from './array';
import MutableEnumerable from './mutable_enumerable';
import Enumerable from './enumerable';

export function removeAt(array, start, len) {
  if ('number' === typeof start) {
    if ((start < 0) || (start >= get(array, 'length'))) {
      throw new EmberError(OUT_OF_RANGE_EXCEPTION);
    }

    // fast case
    if (len === undefined) {
      len = 1;
    }

    array.replace(start, len, EMPTY);
  }

  return array;
}

/**
  This mixin defines the API for modifying array-like objects. These methods
  can be applied only to a collection that keeps its items in an ordered set.
  It builds upon the Array mixin and adds methods to modify the array.
  One concrete implementations of this class include ArrayProxy.

  It is important to use the methods in this class to modify arrays so that
  changes are observable. This allows the binding system in Ember to function
  correctly.

  Note that an Array can change even if it does not implement this mixin.
  For example, one might implement a SparseArray that cannot be directly
  modified, but if its underlying enumerable changes, it will change also.

  @class MutableArray
  @namespace Ember
  @uses Ember.Array
  @uses Ember.MutableEnumerable
  @public
*/
export default Mixin.create(EmberArray, MutableEnumerable, {

  /**
    __Required.__ You must implement this method to apply this mixin.

    This is one of the primitives you must implement to support `Ember.Array`.
    You should replace amt objects started at idx with the objects in the
    passed array. You should also call `this.enumerableContentDidChange()`

    @method replace
    @param {Number} idx Starting index in the array to replace. If
      idx >= length, then append to the end of the array.
    @param {Number} amt Number of elements that should be removed from
      the array, starting at *idx*.
    @param {Array} objects An array of zero or more objects that should be
      inserted into the array at *idx*
    @public
  */
  replace: null,

  /**
    Remove all elements from the array. This is useful if you
    want to reuse an existing array without having to recreate it.

    ```javascript
    let colors = ['red', 'green', 'blue'];

    color.length();   //  3
    colors.clear();   //  []
    colors.length();  //  0
    ```

    @method clear
    @return {Ember.Array} An empty Array.
    @public
  */
  clear() {
    let len = get(this, 'length');
    if (len === 0) {
      return this;
    }

    this.replace(0, len, EMPTY);
    return this;
  },

  /**
    This will use the primitive `replace()` method to insert an object at the
    specified index.

    ```javascript
    let colors = ['red', 'green', 'blue'];

    colors.insertAt(2, 'yellow');  // ['red', 'green', 'yellow', 'blue']
    colors.insertAt(5, 'orange');  // Error: Index out of range
    ```

    @method insertAt
    @param {Number} idx index of insert the object at.
    @param {Object} object object to insert
    @return {Ember.Array} receiver
    @public
  */
  insertAt(idx, object) {
    if (idx > get(this, 'length')) {
      throw new EmberError(OUT_OF_RANGE_EXCEPTION);
    }

    this.replace(idx, 0, [object]);
    return this;
  },

  /**
    Remove an object at the specified index using the `replace()` primitive
    method. You can pass either a single index, or a start and a length.

    If you pass a start and length that is beyond the
    length this method will throw an `OUT_OF_RANGE_EXCEPTION`.

    ```javascript
    let colors = ['red', 'green', 'blue', 'yellow', 'orange'];

    colors.removeAt(0);     // ['green', 'blue', 'yellow', 'orange']
    colors.removeAt(2, 2);  // ['green', 'blue']
    colors.removeAt(4, 2);  // Error: Index out of range
    ```

    @method removeAt
    @param {Number} start index, start of range
    @param {Number} len length of passing range
    @return {Ember.Array} receiver
    @public
  */
  removeAt(start, len) {
    return removeAt(this, start, len);
  },

  /**
    Push the object onto the end of the array. Works just like `push()` but it
    is KVO-compliant.

    ```javascript
    let colors = ['red', 'green'];

    colors.pushObject('black');     // ['red', 'green', 'black']
    colors.pushObject(['yellow']);  // ['red', 'green', ['yellow']]
    ```

    @method pushObject
    @param {*} obj object to push
    @return object same object passed as a param
    @public
  */
  pushObject(obj) {
    this.insertAt(get(this, 'length'), obj);
    return obj;
  },

  /**
    Add the objects in the passed numerable to the end of the array. Defers
    notifying observers of the change until all objects are added.

    ```javascript
    let colors = ['red'];

    colors.pushObjects(['yellow', 'orange']);  // ['red', 'yellow', 'orange']
    ```

    @method pushObjects
    @param {Ember.Enumerable} objects the objects to add
    @return {Ember.Array} receiver
    @public
  */
  pushObjects(objects) {
    if (!(Enumerable.detect(objects) || Array.isArray(objects))) {
      throw new TypeError('Must pass Ember.Enumerable to Ember.MutableArray#pushObjects');
    }
    this.replace(get(this, 'length'), 0, objects);
    return this;
  },

  /**
    Pop object from array or nil if none are left. Works just like `pop()` but
    it is KVO-compliant.

    ```javascript
    let colors = ['red', 'green', 'blue'];

    colors.popObject();   // 'blue'
    console.log(colors);  // ['red', 'green']
    ```

    @method popObject
    @return object
    @public
  */
  popObject() {
    let len = get(this, 'length');
    if (len === 0) {
      return null;
    }

    let ret = objectAt(this, len - 1);
    this.removeAt(len - 1, 1);
    return ret;
  },

  /**
    Shift an object from start of array or nil if none are left. Works just
    like `shift()` but it is KVO-compliant.

    ```javascript
    let colors = ['red', 'green', 'blue'];

    colors.shiftObject();  // 'red'
    console.log(colors);   // ['green', 'blue']
    ```

    @method shiftObject
    @return object
    @public
  */
  shiftObject() {
    if (get(this, 'length') === 0) {
      return null;
    }

    let ret = objectAt(this, 0);
    this.removeAt(0);
    return ret;
  },

  /**
    Unshift an object to start of array. Works just like `unshift()` but it is
    KVO-compliant.

    ```javascript
    let colors = ['red'];

    colors.unshiftObject('yellow');    // ['yellow', 'red']
    colors.unshiftObject(['black']);   // [['black'], 'yellow', 'red']
    ```

    @method unshiftObject
    @param {*} obj object to unshift
    @return object same object passed as a param
    @public
  */
  unshiftObject(obj) {
    this.insertAt(0, obj);
    return obj;
  },

  /**
    Adds the named objects to the beginning of the array. Defers notifying
    observers until all objects have been added.

    ```javascript
    let colors = ['red'];

    colors.unshiftObjects(['black', 'white']);   // ['black', 'white', 'red']
    colors.unshiftObjects('yellow'); // Type Error: 'undefined' is not a function
    ```

    @method unshiftObjects
    @param {Ember.Enumerable} objects the objects to add
    @return {Ember.Array} receiver
    @public
  */
  unshiftObjects(objects) {
    this.replace(0, 0, objects);
    return this;
  },

  /**
    Reverse objects in the array. Works just like `reverse()` but it is
    KVO-compliant.

    @method reverseObjects
    @return {Ember.Array} receiver
     @public
  */
  reverseObjects() {
    let len = get(this, 'length');
    if (len === 0) {
      return this;
    }

    let objects = this.toArray().reverse();
    this.replace(0, len, objects);
    return this;
  },

  /**
    Replace all the receiver's content with content of the argument.
    If argument is an empty array receiver will be cleared.

    ```javascript
    let colors = ['red', 'green', 'blue'];

    colors.setObjects(['black', 'white']);  // ['black', 'white']
    colors.setObjects([]);                  // []
    ```

    @method setObjects
    @param {Ember.Array} objects array whose content will be used for replacing
        the content of the receiver
    @return {Ember.Array} receiver with the new content
    @public
  */
  setObjects(objects) {
    if (objects.length === 0) {
      return this.clear();
    }

    let len = get(this, 'length');
    this.replace(0, len, objects);
    return this;
  },

  // ..........................................................
  // IMPLEMENT Ember.MutableEnumerable
  //

  /**
    Remove all occurrences of an object in the array.

    ```javascript
    let cities = ['Chicago', 'Berlin', 'Lima', 'Chicago'];

    cities.removeObject('Chicago');  // ['Berlin', 'Lima']
    cities.removeObject('Lima');     // ['Berlin']
    cities.removeObject('Tokyo')     // ['Berlin']
    ```

    @method removeObject
    @param {*} obj object to remove
    @return {Ember.Array} receiver
    @public
  */
  removeObject(obj) {
    let loc = get(this, 'length') || 0;
    while (--loc >= 0) {
      let curObject = objectAt(this, loc);

      if (curObject === obj) {
        this.removeAt(loc);
      }
    }
    return this;
  },

  /**
    Push the object onto the end of the array if it is not already
    present in the array.

    ```javascript
    let cities = ['Chicago', 'Berlin'];

    cities.addObject('Lima');    // ['Chicago', 'Berlin', 'Lima']
    cities.addObject('Berlin');  // ['Chicago', 'Berlin', 'Lima']
    ```

    @method addObject
    @param {*} obj object to add, if not already present
    @return {Ember.Array} receiver
    @public
  */
  addObject(obj) {
    let included = this.includes(obj);

    if (!included) {
      this.pushObject(obj);
    }

    return this;
  }
});
<commit_msg>[DOCS] fix NativeArray.clear example<commit_after>/**
@module ember
@submodule ember-runtime
*/

const OUT_OF_RANGE_EXCEPTION = 'Index out of range';
const EMPTY = [];

// ..........................................................
// HELPERS
//

import {
  get,
  Error as EmberError,
  Mixin
} from 'ember-metal';
import EmberArray, { objectAt } from './array';
import MutableEnumerable from './mutable_enumerable';
import Enumerable from './enumerable';

export function removeAt(array, start, len) {
  if ('number' === typeof start) {
    if ((start < 0) || (start >= get(array, 'length'))) {
      throw new EmberError(OUT_OF_RANGE_EXCEPTION);
    }

    // fast case
    if (len === undefined) {
      len = 1;
    }

    array.replace(start, len, EMPTY);
  }

  return array;
}

/**
  This mixin defines the API for modifying array-like objects. These methods
  can be applied only to a collection that keeps its items in an ordered set.
  It builds upon the Array mixin and adds methods to modify the array.
  One concrete implementations of this class include ArrayProxy.

  It is important to use the methods in this class to modify arrays so that
  changes are observable. This allows the binding system in Ember to function
  correctly.

  Note that an Array can change even if it does not implement this mixin.
  For example, one might implement a SparseArray that cannot be directly
  modified, but if its underlying enumerable changes, it will change also.

  @class MutableArray
  @namespace Ember
  @uses Ember.Array
  @uses Ember.MutableEnumerable
  @public
*/
export default Mixin.create(EmberArray, MutableEnumerable, {

  /**
    __Required.__ You must implement this method to apply this mixin.

    This is one of the primitives you must implement to support `Ember.Array`.
    You should replace amt objects started at idx with the objects in the
    passed array. You should also call `this.enumerableContentDidChange()`

    @method replace
    @param {Number} idx Starting index in the array to replace. If
      idx >= length, then append to the end of the array.
    @param {Number} amt Number of elements that should be removed from
      the array, starting at *idx*.
    @param {Array} objects An array of zero or more objects that should be
      inserted into the array at *idx*
    @public
  */
  replace: null,

  /**
    Remove all elements from the array. This is useful if you
    want to reuse an existing array without having to recreate it.

    ```javascript
    let colors = ['red', 'green', 'blue'];

    colors.length;  // 3
    colors.clear(); // []
    colors.length;  // 0
    ```

    @method clear
    @return {Ember.Array} An empty Array.
    @public
  */
  clear() {
    let len = get(this, 'length');
    if (len === 0) {
      return this;
    }

    this.replace(0, len, EMPTY);
    return this;
  },

  /**
    This will use the primitive `replace()` method to insert an object at the
    specified index.

    ```javascript
    let colors = ['red', 'green', 'blue'];

    colors.insertAt(2, 'yellow');  // ['red', 'green', 'yellow', 'blue']
    colors.insertAt(5, 'orange');  // Error: Index out of range
    ```

    @method insertAt
    @param {Number} idx index of insert the object at.
    @param {Object} object object to insert
    @return {Ember.Array} receiver
    @public
  */
  insertAt(idx, object) {
    if (idx > get(this, 'length')) {
      throw new EmberError(OUT_OF_RANGE_EXCEPTION);
    }

    this.replace(idx, 0, [object]);
    return this;
  },

  /**
    Remove an object at the specified index using the `replace()` primitive
    method. You can pass either a single index, or a start and a length.

    If you pass a start and length that is beyond the
    length this method will throw an `OUT_OF_RANGE_EXCEPTION`.

    ```javascript
    let colors = ['red', 'green', 'blue', 'yellow', 'orange'];

    colors.removeAt(0);     // ['green', 'blue', 'yellow', 'orange']
    colors.removeAt(2, 2);  // ['green', 'blue']
    colors.removeAt(4, 2);  // Error: Index out of range
    ```

    @method removeAt
    @param {Number} start index, start of range
    @param {Number} len length of passing range
    @return {Ember.Array} receiver
    @public
  */
  removeAt(start, len) {
    return removeAt(this, start, len);
  },

  /**
    Push the object onto the end of the array. Works just like `push()` but it
    is KVO-compliant.

    ```javascript
    let colors = ['red', 'green'];

    colors.pushObject('black');     // ['red', 'green', 'black']
    colors.pushObject(['yellow']);  // ['red', 'green', ['yellow']]
    ```

    @method pushObject
    @param {*} obj object to push
    @return object same object passed as a param
    @public
  */
  pushObject(obj) {
    this.insertAt(get(this, 'length'), obj);
    return obj;
  },

  /**
    Add the objects in the passed numerable to the end of the array. Defers
    notifying observers of the change until all objects are added.

    ```javascript
    let colors = ['red'];

    colors.pushObjects(['yellow', 'orange']);  // ['red', 'yellow', 'orange']
    ```

    @method pushObjects
    @param {Ember.Enumerable} objects the objects to add
    @return {Ember.Array} receiver
    @public
  */
  pushObjects(objects) {
    if (!(Enumerable.detect(objects) || Array.isArray(objects))) {
      throw new TypeError('Must pass Ember.Enumerable to Ember.MutableArray#pushObjects');
    }
    this.replace(get(this, 'length'), 0, objects);
    return this;
  },

  /**
    Pop object from array or nil if none are left. Works just like `pop()` but
    it is KVO-compliant.

    ```javascript
    let colors = ['red', 'green', 'blue'];

    colors.popObject();   // 'blue'
    console.log(colors);  // ['red', 'green']
    ```

    @method popObject
    @return object
    @public
  */
  popObject() {
    let len = get(this, 'length');
    if (len === 0) {
      return null;
    }

    let ret = objectAt(this, len - 1);
    this.removeAt(len - 1, 1);
    return ret;
  },

  /**
    Shift an object from start of array or nil if none are left. Works just
    like `shift()` but it is KVO-compliant.

    ```javascript
    let colors = ['red', 'green', 'blue'];

    colors.shiftObject();  // 'red'
    console.log(colors);   // ['green', 'blue']
    ```

    @method shiftObject
    @return object
    @public
  */
  shiftObject() {
    if (get(this, 'length') === 0) {
      return null;
    }

    let ret = objectAt(this, 0);
    this.removeAt(0);
    return ret;
  },

  /**
    Unshift an object to start of array. Works just like `unshift()` but it is
    KVO-compliant.

    ```javascript
    let colors = ['red'];

    colors.unshiftObject('yellow');    // ['yellow', 'red']
    colors.unshiftObject(['black']);   // [['black'], 'yellow', 'red']
    ```

    @method unshiftObject
    @param {*} obj object to unshift
    @return object same object passed as a param
    @public
  */
  unshiftObject(obj) {
    this.insertAt(0, obj);
    return obj;
  },

  /**
    Adds the named objects to the beginning of the array. Defers notifying
    observers until all objects have been added.

    ```javascript
    let colors = ['red'];

    colors.unshiftObjects(['black', 'white']);   // ['black', 'white', 'red']
    colors.unshiftObjects('yellow'); // Type Error: 'undefined' is not a function
    ```

    @method unshiftObjects
    @param {Ember.Enumerable} objects the objects to add
    @return {Ember.Array} receiver
    @public
  */
  unshiftObjects(objects) {
    this.replace(0, 0, objects);
    return this;
  },

  /**
    Reverse objects in the array. Works just like `reverse()` but it is
    KVO-compliant.

    @method reverseObjects
    @return {Ember.Array} receiver
     @public
  */
  reverseObjects() {
    let len = get(this, 'length');
    if (len === 0) {
      return this;
    }

    let objects = this.toArray().reverse();
    this.replace(0, len, objects);
    return this;
  },

  /**
    Replace all the receiver's content with content of the argument.
    If argument is an empty array receiver will be cleared.

    ```javascript
    let colors = ['red', 'green', 'blue'];

    colors.setObjects(['black', 'white']);  // ['black', 'white']
    colors.setObjects([]);                  // []
    ```

    @method setObjects
    @param {Ember.Array} objects array whose content will be used for replacing
        the content of the receiver
    @return {Ember.Array} receiver with the new content
    @public
  */
  setObjects(objects) {
    if (objects.length === 0) {
      return this.clear();
    }

    let len = get(this, 'length');
    this.replace(0, len, objects);
    return this;
  },

  // ..........................................................
  // IMPLEMENT Ember.MutableEnumerable
  //

  /**
    Remove all occurrences of an object in the array.

    ```javascript
    let cities = ['Chicago', 'Berlin', 'Lima', 'Chicago'];

    cities.removeObject('Chicago');  // ['Berlin', 'Lima']
    cities.removeObject('Lima');     // ['Berlin']
    cities.removeObject('Tokyo')     // ['Berlin']
    ```

    @method removeObject
    @param {*} obj object to remove
    @return {Ember.Array} receiver
    @public
  */
  removeObject(obj) {
    let loc = get(this, 'length') || 0;
    while (--loc >= 0) {
      let curObject = objectAt(this, loc);

      if (curObject === obj) {
        this.removeAt(loc);
      }
    }
    return this;
  },

  /**
    Push the object onto the end of the array if it is not already
    present in the array.

    ```javascript
    let cities = ['Chicago', 'Berlin'];

    cities.addObject('Lima');    // ['Chicago', 'Berlin', 'Lima']
    cities.addObject('Berlin');  // ['Chicago', 'Berlin', 'Lima']
    ```

    @method addObject
    @param {*} obj object to add, if not already present
    @return {Ember.Array} receiver
    @public
  */
  addObject(obj) {
    let included = this.includes(obj);

    if (!included) {
      this.pushObject(obj);
    }

    return this;
  }
});
<|endoftext|>


<commit_before>/*
 * This is a basic func test for a UseCase application.
 */
YUI({
    useConsoleOutput: true,
    useBrowserConsole: true,
    logInclude: { TestRunner: true }
}).use('node', 'node-event-simulate', 'test', 'console', function (Y) {

         var suite = new Y.Test.Suite("Newsboxes: newsbox2");

         suite.add(new Y.Test.Case({

             "test newsbox2": function() {
                 Y.Assert.areEqual("Contents", Y.all('a').item(0).get('innerHTML'));
                 Y.Assert.areEqual("Source", Y.all('a').item(1).get('innerHTML'));
                 Y.Assert.isTrue(Y.one('.main-sv').hasClass('yui3-scrollview-content'));
             }
         }));

         Y.Test.Runner.add(suite);
});

<commit_msg>add negtive test<commit_after>/*
 * This is a basic func test for a UseCase application.
 */
YUI({
    useConsoleOutput: true,
    useBrowserConsole: true,
    logInclude: { TestRunner: true }
}).use('node', 'node-event-simulate', 'test', 'console', function (Y) {

         var suite = new Y.Test.Suite("Newsboxes: newsbox2");

         suite.add(new Y.Test.Case({

             "test newsbox2": function() {
                 Y.Assert.areEqual("Contents", Y.all('a').item(0).get('innerHTML'));
                 Y.Assert.areEqual("Source", Y.all('a').item(1).get('innerHTML'));
                 Y.Assert.areNotEqual("Ooo, could not fetch stories for", Y.one('#desc').get('innerHTML').match(/Ooo, could not fetch stories for/gi));
                 Y.Assert.isTrue(Y.one('.main-sv').hasClass('yui3-scrollview-content'));
             }
         }));

         Y.Test.Runner.add(suite);
});

<|endoftext|>


<commit_before>var _ = require('underscore');
var LegendColorHelper = require('builder/editor/layers/layer-content-views/legend/form/legend-color-helper');

module.exports = {
  getStyleAttrs: function (styleModel) {
    if (!styleModel) return;

    var fill = styleModel.get('fill');
    var stroke = styleModel.get('stroke');
    if (!fill && !stroke || _.isEmpty(fill) && _.isEmpty(stroke)) return;

    return {
      fill: fill,
      stroke: stroke
    };
  },

  getColorAttribute: function (styleModel) {
    var color = this.getColor(styleModel);
    return color && color.attribute;
  },

  getSizeAttribute: function (styleModel) {
    var size = this.getSize(styleModel);
    return size && size.attribute;
  },

  getColor: function (styleModel) {
    var style = this.getStyleAttrs(styleModel);
    var color = style ? (style.fill && style.fill.color || style.stroke && style.stroke.color) : null;
    return color;
  },

  getSize: function (styleModel) {
    var style = this.getStyleAttrs(styleModel);
    var size = style ? (style.fill && style.fill.size || style.stroke && style.stroke.size) : null;
    return size;
  },

  getStyleCategories: function (styleModel) {
    var color = this.getColor(styleModel);
    var range = 0;

    if (!color) { return []; }

    if (color.range) {
      range = color.range.length;

      return _.range(range).map(function (v, index) {
        return {
          color: color.range[index],
          title: color.domain && LegendColorHelper.unquoteColor(color.domain[index]) || _t('editor.legend.legend-form.others'),
          icon: color.images && color.images[index] || ''
        };
      });
    }

    if (color.fixed) {
      return [{
        color: color.fixed,
        icon: color.image,
        title: ''
      }];
    }

    return [];
  }
};
<commit_msg>New helper method to get range colors<commit_after>var _ = require('underscore');
var LegendColorHelper = require('builder/editor/layers/layer-content-views/legend/form/legend-color-helper');

module.exports = {
  getStyleAttrs: function (styleModel) {
    if (!styleModel) return;

    var fill = styleModel.get('fill');
    var stroke = styleModel.get('stroke');
    if (!fill && !stroke || _.isEmpty(fill) && _.isEmpty(stroke)) return;

    return {
      fill: fill,
      stroke: stroke
    };
  },

  getColorAttribute: function (styleModel) {
    var color = this.getColor(styleModel);
    return color && color.attribute;
  },

  getSizeAttribute: function (styleModel) {
    var size = this.getSize(styleModel);
    return size && size.attribute;
  },

  getColor: function (styleModel) {
    var style = this.getStyleAttrs(styleModel);
    var color = style ? (style.fill && style.fill.color || style.stroke && style.stroke.color) : null;
    return color;
  },

  getSize: function (styleModel) {
    var style = this.getStyleAttrs(styleModel);
    var size = style ? (style.fill && style.fill.size || style.stroke && style.stroke.size) : null;
    return size;
  },

  getColorsFromRange: function (styleModel) {
    var color = this.getColor(styleModel);

    if (!(color && color.range)) { return []; }
    var range = color.range.length;

    return _.range(range).map(function (v, index) {
      return {
        color: color.range[index]
      };
    });
  },

  getStyleCategories: function (styleModel) {
    var color = this.getColor(styleModel);
    var range = 0;

    if (!color) { return []; }

    if (color.range) {
      range = color.range.length;

      return _.range(range).map(function (v, index) {
        return {
          color: color.range[index],
          title: color.domain && LegendColorHelper.unquoteColor(color.domain[index]) || _t('editor.legend.legend-form.others'),
          icon: color.images && color.images[index] || ''
        };
      });
    }

    if (color.fixed) {
      return [{
        color: color.fixed,
        icon: color.image,
        title: ''
      }];
    }

    return [];
  }
};
<|endoftext|>


<commit_before>CT.map.Panorama = CT.Class({
	CLASSNAME: "CT.map.Panorama",
	rotate: function(diff) {
		this.opts.pov.heading += diff;
		this.pan.setPov(this.opts.pov);
	},
	pitch: function(diff) {
		this.opts.pov.pitch += diff;
		this.pan.setPov(this.opts.pov);
	},
	move: function(diffs) {
		for (var axis in diffs)
			this.opts.position[axis] += diffs[axis];
		this.pan.setPosition(this.opts.position);
	},
	update: function(opts) {
		this.opts = CT.merge(opts, this.opts);
		if (opts.position)
			this.pan.setPosition(opts.position);
		if (opts.pov)
			this.pan.setPov(opts.position);
	},
	init: function(opts) {
		this.opts = opts = CT.merge(opts, {
			pov: { // requires: position
				pitch: 0,
				heading: 0
			},
			node: document.body
		});
		this.pan = new google.maps.StreetViewPanorama(opts.node, opts);
	}
});<commit_msg>CT.map.Panorama: zoom(), shift(); working move() (and _move() cb); update() supports pano and zoom (and properly supports position->move()); init() creates service (for use in move())<commit_after>CT.map.Panorama = CT.Class({
	CLASSNAME: "CT.map.Panorama",
	rotate: function(diff) {
		this.opts.pov.heading += diff;
		this.pan.setPov(this.opts.pov);
	},
	pitch: function(diff) {
		this.opts.pov.pitch += diff;
		this.pan.setPov(this.opts.pov);
	},
	zoom: function(z) {
		this.opts.zoom = z;
		this.pan.setZoom(z);
	},
	shift: function(lnum) {
		this.pan.setPano(this.pan.getLinks()[lnum || 0].pano);
	},
	_move: function(data, status) {
		if (status == "OK")
			this.pan.setPano(data.location.pano);
		else
			this.log("move failed!", status);
	},
	move: function(coords, as_diffs) {
		if (as_diffs) {
			this.opts.position = {
				lat: this.pan.position.lat(),
				lng: this.pan.position.lng()
			};
			for (var axis in coords)
				this.opts.position[axis] += coords[axis];
		} else
			this.opts.position = coords;
		this.service.getPanorama({
			radius: 50,
			location: this.opts.position
		}, this._move);
	},
	update: function(opts) {
		this.opts = CT.merge(opts, this.opts);
		if (opts.position)
			this.move(opts.position);
		if (opts.pano)
			this.pan.setPano(opts.pano);
		if (opts.pov)
			this.pan.setPov(opts.position);
		if (opts.zoom)
			this.pan.setZoom(opts.zoom);
	},
	init: function(opts) {
		this.opts = opts = CT.merge(opts, {
			pov: { // requires: position
				pitch: 0,
				heading: 0
			},
			node: document.body
		});
		this.service = new google.maps.StreetViewService();
		this.pan = new google.maps.StreetViewPanorama(opts.node, opts);
	}
});<|endoftext|>


<commit_before>CT.stream.Video = CT.Class({
	CLASSNAME: "CT.stream.Video",
	_buffers: [],
	_nextChunk: 0,
	_sourceOpen: function() {
		this.log("sourceopen");
		this.setSourceBuffer();
	},
	_sourceUpdate: function() {
		this.log("_sourceUpdate", this._buffers.length,
			this.sourceBuffer.updating, this.mediaSource.readyState);
		if (!this.sourceBuffer.updating) {
			if (this._buffers.length)
				this.sourceBuffer.appendBuffer(this._buffers.shift());
			else if (this.mediaSource.readyState == "open") {
				this.mediaSource.endOfStream();
				!this.audio.canplay && this.audio.next();
			}
		}
	},
	setSourceBuffer: function() {
		this.log("setSourceBuffer - exists:", !!this.sourceBuffer);
		if (!this.sourceBuffer) {
			var opts = CT.stream.opts;
			this.sourceBuffer = this.mediaSource.addSourceBuffer(opts.merged
				? opts.codecs.av : opts.codecs.video);
			this.sourceBuffer.mode = 'sequence';
			this.sourceBuffer.addEventListener("updateend", this._sourceUpdate);
		}
	},
	process: function(b64s) {
		if (CT.stream.opts.merged)
			return this.process_single(b64s);
		this.log("process", b64s.video.length,
			b64s.audio && b64s.audio.length);
		var that = this;
		CT.stream.util.b64_to_blob(b64s.video, function(videoblob) {
			that.recorder && that.recorder.remember
				&& that.recorder.remember(videoblob);
			CT.stream.util.blob_to_buffer(videoblob, function(videobuffer) {
				that.audio.push(b64s.audio);
				that._buffers.push(videobuffer);
				that._sourceUpdate();
			});
		});
	},
	process_single: function(dataURL) { // video only, or working av feed :-\
		this.log("process", dataURL.length);
		var that = this;
		CT.stream.util.b64_to_buffer(dataURL, function(buffer) {
			that._buffers.push(buffer);
			that._sourceUpdate();
		});
	},
	start: function() {
		this.canplay = true;
		if (CT.stream.opts.merged || !this.audio.active || this.audio.canplay)
			this._start();
	},
	_start: function() {
		this.log("start (attempting) - paused:", this.video.paused);
		var that = this, prom = this.video.play();
		prom && prom.then(function() {
			that.log("started!!! streaming!");
			that.audio.play();
		})["catch"](function(error) {
			that.log("play failed! awaiting user input (android)", error.message);
			if (CT.stream.opts.requiresInput && !CT.stream.opts.requestedInput) {
				that.audio.node && CT.stream.opts.waiting.push(that.audio.node);
				CT.stream.opts.requestedInput = true;
				(new CT.modal.Prompt({
					cb: CT.stream.opts.startWaiting,
					transition: "fade",
					style: "single-choice",
					data: [ "Play Stream" ],
					prompt: "Ready to stream?"
				})).show();
			}
			CT.stream.opts.waiting.push(that.video);
		});
	},
	_sync: function(e) {
		this.log("carerror", this.video.currentTime);
		var ms = this.video.currentTime * 1000;
		if (this._nextChunk <= ms + CT.stream.opts.sync) {
			this._nextChunk += CT.stream.opts.chunk;
			this.audio.next();
		}
	},
	_miniRecorder: function() {
		var rec = {
			blobs: [],
			remember: function(blob) {
				if (rec.recording)
					rec.blobs.push(blob);
			},
			start: function() {
				rec.recording = true;
			},
			stop: function() {
				rec.recording = false;
			},
			save: function() {
				ConcatenateBlobs(rec.blobs, rec.blobs[0].type,
					invokeSaveAsDialog);
				rec.blobs = [];
			}
		};
		return rec;
	},
	save: function() {
		if (this.recorder) { // stop recording
			this._saveButton.firstChild.classList.remove("buttonActive");
			this.recorder.stop();
			this.recorder.save();
			clearTimeout(this._saveTimer);
			delete this._saveTimer;
			delete this.recorder;
		} else {             // start recording
			this._saveButton.firstChild.classList.add("buttonActive");
			this.recorder = this.opts.stream
				? CT.stream.util.videoRecorder(this.opts.stream)
				: this._miniRecorder();
			this.recorder.start();
			this._saveTimer = setTimeout(this.save, CT.stream.opts.cutoff);
		}
	},
	remove: function() {
		CT.dom.remove(this.node);
		this.audio.remove();
	},
	init: function(opts) {
		this.log("init");
		this.opts = opts = CT.merge(opts, {
			video: null,
			stream: null,
			className: null,
			id: null,
			frame: true,
			videoClass: null,
			videoId: null,
			activeAudio: false,
			attrs: {
				autoplay: true
			}
		});
		if (opts.stream)
			opts.attrs.mozSrcObject = opts.stream;
		this.video = opts.video || CT.dom.video(opts.stream
			&& URL.createObjectURL(opts.stream), opts.videoClass,
			opts.videoId, opts.attrs);
		this.video.on("canplay", this.start);
		this.video.on("pause", this._start);
		this.audio = new CT.stream.Audio(opts.activeAudio, this);
		this._saveButton = CT.dom.img("/img/save.png", null, this.save);
		this.node = opts.frame ? CT.dom.div([
			this.video,
			CT.dom.div([
				this.audio.button,
				CT.dom.pad(2),
				this._saveButton
			], "centered")
		], opts.className, opts.id) : this.video;
		this.node.video = this.video;
		if (opts.stream)
			this.video.volume = 0;
		else {
			this.mediaSource = new MediaSource();
			this.video.src = URL.createObjectURL(this.mediaSource);
			this.mediaSource.addEventListener("sourceopen", this._sourceOpen);
			this.audio.build();
			if (!CT.stream.opts.merged)
				this.video.on("timeupdate", this._sync);
		}
	}
});<commit_msg>log spam<commit_after>CT.stream.Video = CT.Class({
	CLASSNAME: "CT.stream.Video",
	_buffers: [],
	_nextChunk: 0,
	_sourceOpen: function() {
		this.log("sourceopen");
		this.setSourceBuffer();
	},
	_sourceUpdate: function() {
		this.log("_sourceUpdate", this._buffers.length,
			this.sourceBuffer.updating, this.mediaSource.readyState);
		if (!this.sourceBuffer.updating) {
			if (this._buffers.length)
				this.sourceBuffer.appendBuffer(this._buffers.shift());
			else if (this.mediaSource.readyState == "open") {
				this.mediaSource.endOfStream();
				!this.audio.canplay && this.audio.next();
			}
		}
	},
	setSourceBuffer: function() {
		this.log("setSourceBuffer - exists:", !!this.sourceBuffer);
		if (!this.sourceBuffer) {
			var opts = CT.stream.opts;
			this.sourceBuffer = this.mediaSource.addSourceBuffer(opts.merged
				? opts.codecs.av : opts.codecs.video);
			this.sourceBuffer.mode = 'sequence';
			this.sourceBuffer.addEventListener("updateend", this._sourceUpdate);
		}
	},
	process: function(b64s) {
		if (CT.stream.opts.merged)
			return this.process_single(b64s);
		this.log("process", b64s.video.length,
			b64s.audio && b64s.audio.length);
		var that = this;
		CT.stream.util.b64_to_blob(b64s.video, function(videoblob) {
			that.recorder && that.recorder.remember
				&& that.recorder.remember(videoblob);
			CT.stream.util.blob_to_buffer(videoblob, function(videobuffer) {
				that.audio.push(b64s.audio);
				that._buffers.push(videobuffer);
				that._sourceUpdate();
			});
		});
	},
	process_single: function(dataURL) { // video only, or working av feed :-\
		this.log("process", dataURL.length);
		var that = this;
		CT.stream.util.b64_to_buffer(dataURL, function(buffer) {
			that._buffers.push(buffer);
			that._sourceUpdate();
		});
	},
	start: function() {
		this.canplay = true;
		if (CT.stream.opts.merged || !this.audio.active || this.audio.canplay)
			this._start();
	},
	_start: function() {
		this.log("start (attempting) - paused:", this.video.paused);
		var that = this, prom = this.video.play();
		prom && prom.then(function() {
			that.log("started!!! streaming!");
			that.audio.play();
		})["catch"](function(error) {
			that.log("play failed! awaiting user input (android)", error.message);
			if (CT.stream.opts.requiresInput && !CT.stream.opts.requestedInput) {
				that.audio.node && CT.stream.opts.waiting.push(that.audio.node);
				CT.stream.opts.requestedInput = true;
				(new CT.modal.Prompt({
					cb: CT.stream.opts.startWaiting,
					transition: "fade",
					style: "single-choice",
					data: [ "Play Stream" ],
					prompt: "Ready to stream?"
				})).show();
			}
			CT.stream.opts.waiting.push(that.video);
		});
	},
	_sync: function(e) {
		var ms = this.video.currentTime * 1000;
		if (this._nextChunk <= ms + CT.stream.opts.sync) {
			this._nextChunk += CT.stream.opts.chunk;
			this.audio.next();
		}
	},
	_miniRecorder: function() {
		var rec = {
			blobs: [],
			remember: function(blob) {
				if (rec.recording)
					rec.blobs.push(blob);
			},
			start: function() {
				rec.recording = true;
			},
			stop: function() {
				rec.recording = false;
			},
			save: function() {
				ConcatenateBlobs(rec.blobs, rec.blobs[0].type,
					invokeSaveAsDialog);
				rec.blobs = [];
			}
		};
		return rec;
	},
	save: function() {
		if (this.recorder) { // stop recording
			this._saveButton.firstChild.classList.remove("buttonActive");
			this.recorder.stop();
			this.recorder.save();
			clearTimeout(this._saveTimer);
			delete this._saveTimer;
			delete this.recorder;
		} else {             // start recording
			this._saveButton.firstChild.classList.add("buttonActive");
			this.recorder = this.opts.stream
				? CT.stream.util.videoRecorder(this.opts.stream)
				: this._miniRecorder();
			this.recorder.start();
			this._saveTimer = setTimeout(this.save, CT.stream.opts.cutoff);
		}
	},
	remove: function() {
		CT.dom.remove(this.node);
		this.audio.remove();
	},
	init: function(opts) {
		this.log("init");
		this.opts = opts = CT.merge(opts, {
			video: null,
			stream: null,
			className: null,
			id: null,
			frame: true,
			videoClass: null,
			videoId: null,
			activeAudio: false,
			attrs: {
				autoplay: true
			}
		});
		if (opts.stream)
			opts.attrs.mozSrcObject = opts.stream;
		this.video = opts.video || CT.dom.video(opts.stream
			&& URL.createObjectURL(opts.stream), opts.videoClass,
			opts.videoId, opts.attrs);
		this.video.on("canplay", this.start);
		this.video.on("pause", this._start);
		this.audio = new CT.stream.Audio(opts.activeAudio, this);
		this._saveButton = CT.dom.img("/img/save.png", null, this.save);
		this.node = opts.frame ? CT.dom.div([
			this.video,
			CT.dom.div([
				this.audio.button,
				CT.dom.pad(2),
				this._saveButton
			], "centered")
		], opts.className, opts.id) : this.video;
		this.node.video = this.video;
		if (opts.stream)
			this.video.volume = 0;
		else {
			this.mediaSource = new MediaSource();
			this.video.src = URL.createObjectURL(this.mediaSource);
			this.mediaSource.addEventListener("sourceopen", this._sourceOpen);
			this.audio.build();
			if (!CT.stream.opts.merged)
				this.video.on("timeupdate", this._sync);
		}
	}
});<|endoftext|>


<commit_before>/**
 * @file AttributeClassOnly attributes
 */
/*#ifndef(UMD)*/
"use strict";
/*global _gpfA*/ // gpf.attributes
/*global _gpfAttribute*/ // Shortcut for gpf.attributes.Attribute
/*global _gpfDefAttr*/ // gpf.define for attributes
/*global _gpfErrorDeclare*/ // Declare new gpf.Error names
/*global _gpfGetClassDefinition*/ // Get GPF class definition for a constructor
/*global _gpfIgnore*/ // Helper to remove unused parameter warning
/*#endif*/

_gpfErrorDeclare("a_attributes", {
    onlyForAttributeClass:
        "The attribute {attributeName} can be used only on an Attribute class",
    onlyOnClassForAttributeClass:
        "The attribute {attributeName} must be used on Class",
    classOnlyAttribute:
        "The attribute {attributeName} can be used only for Class",
    memberOnlyAttribute:
        "The attribute {attributeName} can be used only for members",
    uniqueAttributeConstraint:
        "Attribute {attributeName} already defined on {className}",
    uniqueMemberAttributeConstraint:
        "Attribute {attributeName} already defined on {className}::{memberName}"
});

/* istanbul ignore next */ // Abstract method
/**
 * Throws an exception if target attribute can't be applied to objPrototype
 *
 * @param {gpf.attributes.Attribute} targetAttribute
 * @param {Object} objPrototype
 */
function _GpfAttrOnlyCheck (targetAttribute, objPrototype) {
    _gpfIgnore(targetAttribute, objPrototype);
    throw gpf.Error.abstractMethod();
}

var
    /**
     * Restricts the usage of an attribute to an attribute class only
     *
     * @class gpf.attributes.AttrClassOnlyAttribute
     * @extends gpf.attributes.Attribute
     */
    _GpfAttrOnly = _gpfDefAttr("AttrClassOnlyAttribute", {
        "#": {

            _alterPrototype: function (objPrototype) {
                if (!(objPrototype instanceof _gpfAttribute)) {
                    throw gpf.Error.onlyForAttributeClass({
                        attributeName: _gpfGetClassDefinition(this.constructor)._name
                    });
                }
                if (this._member !== "Class") {
                    throw gpf.Error.onlyOnClassForAttributeClass({
                        attributeName: _gpfGetClassDefinition(this.constructor)._name
                    });
                }
            }

        }
    }),

    /**
     * Provide mechanism to validate the USE of an attribute
     *
     * @class gpf.attributes.AttrConstraintAttribute
     * @extends gpf.attributes.AttrClassOnlyAttribute
     */
    _gpfAttrConstraint = _gpfDefAttr("AttrConstraintAttribute", _GpfAttrOnly, {
        "~": {

            // Name used to remember the original _alterPrototype handler
            originalAlterPrototype: "_alterPrototype:checked"

        },
        "-": {

            /**
             * Check that all attribute constraints are respected before calling the original _alterPrototype
             *
             * @param {Object} objPrototype
             * @this {gpf.attributes.Attribute} the child class attribute
             */
            _checkAndAlterPrototype: function (objPrototype) {
                var me = this,
                    statics = _gpfA.AttrConstraintAttribute,
                    originalAlterPrototype = statics.originalAlterPrototype,
                    attributes = new _gpfA.Map(me);
                // Get constraints set for THIS attribute
                attributes.filter(_gpfAttrConstraint).forEach(function (constraintAttributes) {
                    constraintAttributes.forEach(function (attribute) {
                        attribute._check(me, objPrototype);
                    });
                });
                // OK, call _alterPrototype
                me[originalAlterPrototype](objPrototype);
            }

        },
        "#": {

            // @inheritdoc _GpfAttrOnlyCheck
            _check: _GpfAttrOnlyCheck,

            // @inheritdoc gpf.attributes.Attribute#_alterPrototype
            _alterPrototype: function (objPrototype) {
                var statics = _gpfA.AttrConstraintAttribute,
                    originalAlterPrototype = statics.originalAlterPrototype;

                // Inherited method will take care of checking attribute class
                this._super(objPrototype);

                /**
                 * Several constraint attributes might be defined, check if the _alterPrototype has already been
                 * overridden
                 */
                if (undefined === objPrototype[originalAlterPrototype]) {
                    objPrototype[originalAlterPrototype] = objPrototype._alterPrototype;
                    objPrototype._alterPrototype = this._checkAndAlterPrototype;
                }
            }

        }
    });

/**
 * Used on attribute classes to mark them as class attribute (i.e. they can't be used on members)
 *
 * @class gpf.attributes.ClassAttributeAttribute
 * @extends gpf.attributes.AttrConstraintAttribute
 * @alias gpf.$ClassAttribute
 */
_gpfDefAttr("$ClassAttribute", _gpfAttrConstraint, {
    "#": {

        // @inheritdoc gpf.attributes.AttrConstraintAttribute#_check
        _check: function (targetAttribute, objPrototype) {
            _gpfIgnore(objPrototype);
            if (targetAttribute._member !== "Class") {
                var attributeClass = targetAttribute.constructor,
                    attributeClassDef = _gpfGetClassDefinition(attributeClass);
                throw gpf.Error.classOnlyAttribute({
                    attributeName: attributeClassDef._name
                });
            }
        }

    }
});

/**
 * Used on attribute classes to mark them as member attribute (i.e. they can't be used on Class)
 *
 * @class gpf.attributes.MemberAttributeAttribute
 * @extends gpf.attributes.AttrConstraintAttribute
 * @alias gpf.$MemberAttribute
 */
_gpfDefAttr("$MemberAttribute", _gpfAttrConstraint, {
    "#": {

        // @inheritdoc gpf.attributes.AttrConstraintAttribute#_check
        _check: function (targetAttribute, objPrototype) {
            _gpfIgnore(objPrototype);
            if (targetAttribute._member === "Class") {
                var attributeClass = targetAttribute.constructor,
                    attributeClassDef = _gpfGetClassDefinition(attributeClass);
                throw gpf.Error.memberOnlyAttribute({
                    attributeName: attributeClassDef._name
                });
            }
        }
    }
});

/**
 * Used on attribute classes to mark them as unique through the class hierarchy or per member.
 * If one try to define it more than once, an error is raised
 *
 * @class gpf.attributes.UniqueAttributeAttribute
 * @extends gpf.attributes.AttrConstraintAttribute
 * @alias gpf.$UniqueAttribute
 */
_gpfDefAttr("$UniqueAttribute", _gpfAttrConstraint, {
    "-": {

        // The attribute is unique for the whole class when true or per member when false
        _classScope: true

    },
    "#": {

        // @inheritdoc gpf.attributes.AttrConstraintAttribute#_check
        _check: function (targetAttribute, objPrototype) {
            var objectClass,
                objectClassDef,
                objectClassAttributes,
                attributeClass,
                attributeClassDef,
                attributesInObj,
                member = targetAttribute._member;

            // Get object class definition & attributes
            objectClass = objPrototype.constructor;
            objectClassDef = _gpfGetClassDefinition(objectClass);
            objectClassAttributes = new _gpfA.Map(objectClass);

            // Get attribute class definition & attributes
            attributeClass = targetAttribute.constructor;
            attributeClassDef = _gpfGetClassDefinition(attributeClass);

            attributesInObj = objectClassAttributes.filter(attributeClass);

            // Don't forget that targetAttribute is already added to the object
            if (this._classScope) {
                if (1 < attributesInObj.getCount()) {
                    throw gpf.Error.uniqueAttributeConstraint({
                        attributeName: attributeClassDef._name,
                        className: objectClassDef._name
                    });
                }
            } else if (1 < attributesInObj.getMemberAttributes(member).getItemsCount()) {
                throw gpf.Error.uniqueMemberAttributeConstraint({
                    attributeName: attributeClassDef._name,
                    className: objectClassDef._name,
                    memberName: member
                });
            }
        }

    },
    "+": {

        /**
         * @param {Boolean} [classScope=true] classScope True to set limit to one instance per class (including
         * hierarchy) or false to limit to one instance per member.
         */
        constructor: function (classScope) {
            if (undefined !== classScope) {
                this._classScope = true === classScope;
            }
        }

    }
});
<commit_msg>Improves maintainability<commit_after>/**
 * @file AttributeClassOnly attributes
 */
/*#ifndef(UMD)*/
"use strict";
/*global _gpfA*/ // gpf.attributes
/*global _gpfAttribute*/ // Shortcut for gpf.attributes.Attribute
/*global _gpfDefAttr*/ // gpf.define for attributes
/*global _gpfErrorDeclare*/ // Declare new gpf.Error names
/*global _gpfGetClassDefinition*/ // Get GPF class definition for a constructor
/*global _gpfIgnore*/ // Helper to remove unused parameter warning
/*#endif*/

_gpfErrorDeclare("attributes/attributes", {
    onlyForAttributeClass:
        "The attribute {attributeName} can be used only on an Attribute class",
    onlyOnClassForAttributeClass:
        "The attribute {attributeName} must be used on Class",
    classOnlyAttribute:
        "The attribute {attributeName} can be used only for Class",
    memberOnlyAttribute:
        "The attribute {attributeName} can be used only for members",
    uniqueAttributeConstraint:
        "Attribute {attributeName} already defined on {className}",
    uniqueMemberAttributeConstraint:
        "Attribute {attributeName} already defined on {className}::{memberName}"
});

/* istanbul ignore next */ // Abstract method
/**
 * Throws an exception if target attribute can't be applied to objPrototype
 *
 * @param {gpf.attributes.Attribute} targetAttribute
 * @param {Object} objPrototype
 */
function _GpfAttrOnlyCheck (targetAttribute, objPrototype) {
    _gpfIgnore(targetAttribute, objPrototype);
    throw gpf.Error.abstractMethod();
}

var
    /**
     * Restricts the usage of an attribute to an attribute class only
     *
     * @class gpf.attributes.AttrClassOnlyAttribute
     * @extends gpf.attributes.Attribute
     */
    _GpfAttrOnly = _gpfDefAttr("AttrClassOnlyAttribute", {
        "-": {

            /**
             * Return current attribute name
             *
             * @return {String}
             */
            _getAttributeName: function () {
                return _gpfGetClassDefinition(this.constructor)._name;
            },

            _checkTargetClassIsAttribute: function (objPrototype) {
                if (!(objPrototype instanceof _gpfAttribute)) {
                    throw gpf.Error.onlyForAttributeClass({
                        attributeName: this._getAttributeName()
                    });
                }
            },

            _checkMemberIsClass: function () {
                if (this._member !== "Class") {
                    throw gpf.Error.onlyOnClassForAttributeClass({
                        attributeName: this._getAttributeName()
                    });
                }
            }

        },

        "#": {

            _alterPrototype: function (objPrototype) {
                this._checkTargetClassIsAttribute(objPrototype);
                this._checkMemberIsClass();
            }

        }
    }),

    /**
     * Provide mechanism to validate the USE of an attribute
     *
     * @class gpf.attributes.AttrConstraintAttribute
     * @extends gpf.attributes.AttrClassOnlyAttribute
     */
    _gpfAttrConstraint = _gpfDefAttr("AttrConstraintAttribute", _GpfAttrOnly, {
        "~": {

            // Name used to remember the original _alterPrototype handler
            originalAlterPrototype: "_alterPrototype:checked"

        },
        "-": {

            /**
             * Check that all attribute constraints are respected before calling the original _alterPrototype
             *
             * @param {Object} objPrototype
             * @this {gpf.attributes.Attribute} the child class attribute
             */
            _checkAndAlterPrototype: function (objPrototype) {
                var me = this,
                    statics = _gpfA.AttrConstraintAttribute,
                    originalAlterPrototype = statics.originalAlterPrototype,
                    attributes = new _gpfA.Map(me);
                // Get constraints set for THIS attribute
                attributes.filter(_gpfAttrConstraint).forEach(function (constraintAttributes) {
                    constraintAttributes.forEach(function (attribute) {
                        attribute._check(me, objPrototype);
                    });
                });
                // OK, call _alterPrototype
                me[originalAlterPrototype](objPrototype);
            }

        },
        "#": {

            // @inheritdoc _GpfAttrOnlyCheck
            _check: _GpfAttrOnlyCheck,

            // @inheritdoc gpf.attributes.Attribute#_alterPrototype
            _alterPrototype: function (objPrototype) {
                var statics = _gpfA.AttrConstraintAttribute,
                    originalAlterPrototype = statics.originalAlterPrototype;

                // Inherited method will take care of checking attribute class
                this._super(objPrototype);

                /**
                 * Several constraint attributes might be defined, check if the _alterPrototype has already been
                 * overridden
                 */
                if (undefined === objPrototype[originalAlterPrototype]) {
                    objPrototype[originalAlterPrototype] = objPrototype._alterPrototype;
                    objPrototype._alterPrototype = this._checkAndAlterPrototype;
                }
            }

        }
    });

/**
 * Used on attribute classes to mark them as class attribute (i.e. they can't be used on members)
 *
 * @class gpf.attributes.ClassAttributeAttribute
 * @extends gpf.attributes.AttrConstraintAttribute
 * @alias gpf.$ClassAttribute
 */
_gpfDefAttr("$ClassAttribute", _gpfAttrConstraint, {
    "#": {

        // @inheritdoc gpf.attributes.AttrConstraintAttribute#_check
        _check: function (targetAttribute, objPrototype) {
            _gpfIgnore(objPrototype);
            if (targetAttribute._member !== "Class") {
                var attributeClass = targetAttribute.constructor,
                    attributeClassDef = _gpfGetClassDefinition(attributeClass);
                throw gpf.Error.classOnlyAttribute({
                    attributeName: attributeClassDef._name
                });
            }
        }

    }
});

/**
 * Used on attribute classes to mark them as member attribute (i.e. they can't be used on Class)
 *
 * @class gpf.attributes.MemberAttributeAttribute
 * @extends gpf.attributes.AttrConstraintAttribute
 * @alias gpf.$MemberAttribute
 */
_gpfDefAttr("$MemberAttribute", _gpfAttrConstraint, {
    "#": {

        // @inheritdoc gpf.attributes.AttrConstraintAttribute#_check
        _check: function (targetAttribute, objPrototype) {
            _gpfIgnore(objPrototype);
            if (targetAttribute._member === "Class") {
                var attributeClass = targetAttribute.constructor,
                    attributeClassDef = _gpfGetClassDefinition(attributeClass);
                throw gpf.Error.memberOnlyAttribute({
                    attributeName: attributeClassDef._name
                });
            }
        }
    }
});

/**
 * Used on attribute classes to mark them as unique through the class hierarchy or per member.
 * If one try to define it more than once, an error is raised
 *
 * @class gpf.attributes.UniqueAttributeAttribute
 * @extends gpf.attributes.AttrConstraintAttribute
 * @alias gpf.$UniqueAttribute
 */
_gpfDefAttr("$UniqueAttribute", _gpfAttrConstraint, {
    "-": {

        // The attribute is unique for the whole class when true or per member when false
        _classScope: true

    },
    "#": {

        // @inheritdoc gpf.attributes.AttrConstraintAttribute#_check
        _check: function (targetAttribute, objPrototype) {
            var objectClass,
                objectClassDef,
                objectClassAttributes,
                attributeClass,
                attributeClassDef,
                attributesInObj,
                member = targetAttribute._member;

            // Get object class definition & attributes
            objectClass = objPrototype.constructor;
            objectClassDef = _gpfGetClassDefinition(objectClass);
            objectClassAttributes = new _gpfA.Map(objectClass);

            // Get attribute class definition & attributes
            attributeClass = targetAttribute.constructor;
            attributeClassDef = _gpfGetClassDefinition(attributeClass);

            attributesInObj = objectClassAttributes.filter(attributeClass);

            // Don't forget that targetAttribute is already added to the object
            if (this._classScope) {
                if (1 < attributesInObj.getCount()) {
                    throw gpf.Error.uniqueAttributeConstraint({
                        attributeName: attributeClassDef._name,
                        className: objectClassDef._name
                    });
                }
            } else if (1 < attributesInObj.getMemberAttributes(member).getItemsCount()) {
                throw gpf.Error.uniqueMemberAttributeConstraint({
                    attributeName: attributeClassDef._name,
                    className: objectClassDef._name,
                    memberName: member
                });
            }
        }

    },
    "+": {

        /**
         * @param {Boolean} [classScope=true] classScope True to set limit to one instance per class (including
         * hierarchy) or false to limit to one instance per member.
         */
        constructor: function (classScope) {
            if (undefined !== classScope) {
                this._classScope = true === classScope;
            }
        }

    }
});
<|endoftext|>


<commit_before>var bug566510_TabContextMenu = {
    /**
     * It just hooks on onpopupshowing event and then makes bold all menuitems
     * that support multiselect.
     */
    updateContextMenu: function(event) {
        TabContextMenu.updateContextMenu(event);

        var style = "font-weight: normal;";
        var menuItemIDs = [
            "context_reloadTab",
            "context_pinTab",
            "context_unpinTab",
            "context_tabViewMenu",
            "context_openTabInWindow",
            "context_closeOtherTabs",
            "context_bookmarkAllTabs",
            "context_closeTab"
        ];

        var contextTabs = TabContextMenu.contextTab.multiselected ? gBrowser.selectedTabs : [TabContextMenu.contextTab];
        if (contextTabs.length > 1)
            style = "font-weight: bold;";
        for (var i = 0; i < menuItemIDs.length; i++) {
            var id = menuItemIDs[i];
            var menuItem = document.getElementById(id);
            menuItem.setAttribute("style", style);
        }
    }
}
<commit_msg>Change: update 'multiselection' attribute on showing tab context menu<commit_after>var bug566510_TabContextMenu = {
    /**
     * It just hooks on popupshowing event of tab context menu and updates
     * the 'multiselection' attribute of (all) elements.
     */
    updateContextMenu: function(event) {
        TabContextMenu.updateContextMenu(event);

        var menuItems = document.getElementsByAttribute("multiselection", "*");
        var contextTabs = TabContextMenu.contextTab.multiselected ? gBrowser.selectedTabs : [TabContextMenu.contextTab];
        var multiselection = contextTabs.length > 1;
        for (var i = 0; i < menuItems.length; i++) {
            menuItems[i].setAttribute("multiselection", multiselection);
        }
    }
}
<|endoftext|>