<commit_before>// Collect accelerometer samples and save to MongoDB collection named "samples".
// Copyright 2014 Juan Pineda. See LICENSE (MIT) file.

Samples = new Meteor.Collection("samples"); // The sample collection
Counts = new Meteor.Collection("counts");   // Non Mongo counts collection
Timestamps = new Meteor.Collection("timestamps");

var time_limit = 60;

// Helper to get current user name

var get_username = function() {
  var u = Meteor.user();
  return u ? u.username : "";;
}

// Helper to formulate query

var user_filter = function (username) {
  return {username: username==undefined ? "" : username};
}

if (Meteor.isClient) {

  // Helper to get session variables

  Handlebars.registerHelper('sessionvar',function(v){
    var val = Session.get(v);
    return val;
  });

  Handlebars.registerHelper('origin',function(){
    return window.location.origin
  });

  // Body functions

  Template.body.activetab = function (t) {
    return (t == Session.get("tabs")) || (t == "#review" && !Session.get("tabs"));
  }

  Template.body.username = function () {
    return get_username();
  }

  Template.body.has_username = function () {
    var username = get_username();
    return username != "" && username != undefined;
  }

  Template.body.events({
    'click button#clear': function () {
      var filter = user_filter(get_username());
      if (filter.username == undefined && !confirm("Really?")) {
        return;
      }
      Meteor.call("delete_samples", filter);
    }
  });

  // Server timestamp display for debugging purposes

  Template.timestamp.timestamp = function () {
    var ts = get_timestamp();
    if (!ts) return;
    return ts - Date.now();
  };

  var get_timestamp = function () {
    t = Timestamps.findOne();
    if (!t) return 0;
    return t.timestamp;
  }

  Meteor.subscribe("timestamps", 1);

  // Recent samples display

  var timestamps = [];
  var format_sample = function (s) {
    r = sprintf("%4d %d  %6.2f %6.2f %6.2f   %6.1f %6.1f %6.1f",
      s['t']-timestamps[0], s['t'],
      s['x'], s['y'], s['z'],
      s['a'], s['b'], s['c']);
    timestamps[0] = s['t'];
    return r;
  }

  Template.recentdata.recentsamples = function () {
    var s = Samples.findOne(user_filter(get_username()), {sort: {created_at: -1}, limit: 1});
    if (s != null) {
      if (!timestamps.length) {
        timestamps.unshift(s["samples"][0]['t']);
        timestamps.unshift(timestamps[0]);
      } else if (timestamps[0] == s["samples"][19]['t']) {
        timestamps[0] = timestamps[1];
      } else {
      }
      timestamps.unshift(timestamps[0]);
      r = _.map(s["samples"], format_sample );
      timestamps.pop();
      return r;
    } else {
      return [];
    }
  };

  Template.recentdata.time_limit = function () {
    return time_limit;
  }

  // Handle history via location hash

  $(window).bind('popstate', function() {
    Session.set("tabs", location.hash);
  });

  // Navigation tab wiring

  Template.body.rendered = function () {

    // Handler to set tabs session variable whenever tab is shown

    $('#maintabs a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
      Session.set("tabs", e.target.hash);
    });

    // Explicitly show tab content
    // because the Bootstrap data-toggle mechanism doesn't work with Meteor
    // because tab content is not propery rendered before the toggle is executed.
    // Maybe unnecessary with Meteor 0.8 because it does not rebuild the entire DOM.

    if (Session.get("tabs")) {
      $('#maintabs ' + Session.get("tabs") + "tab").tab("show");
    } else {
      $('#maintabs a[data-toggle="tab"]:first').tab("show"); // On startup initalize first tab.
    }
  }

  // Live display of number of samples

  Template.nsamples.nsamples = function () {
    var s = Counts.findOne(); // This can return null during startup!
    if (!s) return "nsamples: Oops!";
    return s.count;
  };

  var is_recording = function () {
    var u = Meteor.user();
    if (!u || !u.profile) return false;
    var ts = Timestamps.findOne();
    if (!ts) return false;
    return ts.timestamp - u.profile.recording < time_limit*1000;
  }

  Template.nsamples.recording = function () {
    return is_recording();
  }

  // Set the recording state

  var set_recording = function(recording) {
    if (recording && Counts.findOne() && Counts.findOne().count) {
      alert("Please delete samples first.");
      if (Meteor.user().username != "Juan") return;
    }
    recording_val = recording ? Date.now() : null;
    Meteor.users.update(Meteor.userId(), {$set: {'profile.recording': recording_val}});
  }

  // The recording button

  Template.recording.recording = function () {
    return is_recording();
  }

  Template.recording.events({
    'click button#startstop': function () {
      set_recording(!Template.recording.recording());
    }
  });

  Template.status.status = function () {
    s = Meteor.status()['status']
    if (s=="connected") {
      r = 'muted' ;
      $('#statusModal').modal('hide');
    } else if (s=="connecting") {
      r = 'text-warning' ;
      $('#statusModal').modal('show');
    } else {
      r = 'text-error';
      s = s + '!'
      $('#statusModal').modal('show');
    }
    if (Meteor.status()['reason']) s += " " + Meteor.status()['reason']
    if (Meteor.status()['retryCount']) s += " (" + Meteor.status()['retryCount'] + ")"
    $('#statusModal #statusModalLabel').html("Server status: " + s + "");
    return new Handlebars.SafeString("" + s + "");
  }

  Deps.autorun(Template.status.status); // Not sure why this one is needed

  // At startup set up device motion event handler.

  Deps.autorun(function () {
    var username = get_username();
    if (!username) return;
    Meteor.subscribe ("counts", username);
  });

  Deps.autorun(function () {
    var username = get_username();
    if (!username) return;
    Meteor.subscribe ("recent_samples", username);
  });

  Meteor.startup(function () {

    Accounts.ui.config({
      passwordSignupFields: 'USERNAME_ONLY'
    });

    var timestamp = 0;
    var samples = [];

    if (window.DeviceMotionEvent != undefined) {

      // Device motion event service routine!

      window.ondevicemotion = function(e) {

        var s = "";

        // Measure sample interval and display on page

        var t = Date.now();
        s += sprintf("Sample period:     %3.0f ms
", (t - timestamp));
        s += sprintf("API sample period: %3.0f ms

", Math.floor(e.interval*1000));
        timestamp = t

        // Create the sample

        var sample = {}
        sample.x = e.accelerationIncludingGravity.x;
        sample.y = e.accelerationIncludingGravity.y;
        sample.z = e.accelerationIncludingGravity.z;
        if ( e.rotationRate ) {
          sample.a = e.rotationRate.alpha;
          sample.b = e.rotationRate.beta;
          sample.c = e.rotationRate.gamma;
        }
        sample.t = t;

        // Live update (Only when not recording)

        if (!is_recording() || Session.get("tabs") != "#collect") {
          samples = [];

          s += sprintf("Timestamp: %f

", sample['t']);
          s += sprintf("Acc:  %6.2f %6.2f %6.2f
", sample['x'], sample['y'], sample['z']);
          if ( e.rotationRate ) {
          s += sprintf("Rot:  %6.1f %6.1f %6.1f
", sample['a'], sample['b'], sample['c']);
          }

          $("#accxyz").html(s);
          return;
        }
        $("#accxyz").html("Collecting...");

        // Every 20 samples save record in mongoDB.

        samples.push(sample);
        if (samples.length >= 20) {
          created_at = t;
          var username = get_username();
          Samples.insert({samples: samples, created_at: created_at, username: username});
          samples = [];
        }
      }
    }
  });
}

// Server

if (Meteor.isServer) {

  Samples.allow({
    insert: function (id, doc) { // Allow user to delete their own documents
      return doc.username == Meteor.user().username;
    }
  });

  var set_recording = function(username, recording) {
    u = Meteor.users.findOne({username: username});
    recording_val = recording ? Date.now() : null;
    Meteor.users.update(u._id, {$set: {'profile.recording': recording_val}});
  };

  var get_recording = function(username, recording) {
    u = Meteor.users.findOne({username: username});
    if (!u) return undefined;
    if (!u.profile) return undefined;
    return u.profile.recording;
  };

  Meteor.methods({
    // Because Meteor does not allow client to delete multiple documents
    delete_samples: function(filter, n) {
      if (n==undefined) {
        Samples.remove(filter);
      } else if (n>0) {
        var ids = Samples.find(filter,{sort: {created_at: 1}, limit: n, fields: {_id: 1}}).fetch();
        ids = ids.map(function(i){return i._id});
        Samples.remove({_id: {'$in': ids}});
        return
      }
    },
  });

  Meteor.publish("counts", function (username) {
    var initializing = true; // Not sure if this is needed. Copied from example.
    var self = this; // Needed to capture value of this for function closures.
    var s = Samples.find(user_filter(username), {fields: {_id: 1}});
    var handle = s.observeChanges({
      added : function (id) {
        if (!initializing) {
          self.changed("counts", username, {count: s.count()});
        }
      },
      removed : function (id) {
        if (!initializing) {
          self.changed("counts", username, {count: s.count()});
        }
      }
    });
    var count = s.count();
    initializing = false;
    self.added("counts", username, {count: count});
    self.ready();
    self.onStop(function () {
      handle.stop();
    });
  });

  Meteor.publish("recent_samples", function (username) {
    var s = Samples.find(user_filter(username), {sort: {created_at: -1}, limit: 1});
    s.rewind();
    return s;
  });

  Meteor.publish("timestamps", function (x) {
    var self = this; // Needed to capture value of this for function closures.
    self.added("timestamps", "0", {timestamp: Date.now()});
    Meteor.setInterval(function (){
      self.changed("timestamps", "0", {timestamp: Date.now()});
    }, 1000)
    self.ready();
  });

  Meteor.startup(function () {
    Samples._ensureIndex({created_at: -1, username: 1})
  });
}

// Server routes

Router.configure({
  notFoundTemplate: 'notFound' // this will render
});

Router.map(function () {
  this.route('body', {
    path: '/'
  });
});

Router.map(function () {
  this.route('json', {
    where: 'server',
    path: '/json/:username',

    action: function () {
      var username = this.params.username;
      // set recording, but not too frequently because changes to user record bog down browser
      var recording = get_recording(username);
      if (!recording || recording && Date.now() > recording + time_limit/2*1000) {
        set_recording(username, Date.now());
      }
      var filter = {username: username};

      var t = this.params.t;
      if (t != undefined) {
        filter['created_at'] = {$gt : parseInt(t)};
      }

      var seconds = 5; // Need at least 2 seconds to allow for occasional catchup
      var skip = 0; // Default skip nothing
      var nrecords = Samples.find(filter).count();
      skip = Math.max(0, nrecords-seconds);

      var samples = Samples.find(filter, {sort: {created_at: 1}, skip: skip});

      // Get samples and form response

      var nrecords_fetched = 0;
      var l = [];
      samples.forEach(function (s) {
        l = l.concat(s.samples);
        nrecords_fetched++;
      });

      this.response.writeHead(200, {'Content-Type': 'application/json', 'Content-Disposition': 'attachment'});
      this.response.end(JSON.stringify(l));

      // Delete samples as we stream them

      var total_records = Samples.find({username: username}).count();
      var ndelete = 0;
      if (total_records > time_limit) ndelete = total_records - time_limit;
//      else if (total_records - nrecords_fetched > 10) ndelete = nrecords_fetched;
      Meteor.call("delete_samples", {username: username}, ndelete);

    }
  });
});
<commit_msg>Reset recording timestamp upon delete.<commit_after>// Collect accelerometer samples and save to MongoDB collection named "samples".
// Copyright 2014 Juan Pineda. See LICENSE (MIT) file.

Samples = new Meteor.Collection("samples"); // The sample collection
Counts = new Meteor.Collection("counts");   // Non Mongo counts collection
Timestamps = new Meteor.Collection("timestamps");

var time_limit = 60;

// Helper to get current user name

var get_username = function() {
  var u = Meteor.user();
  return u ? u.username : "";;
}

// Helper to formulate query

var user_filter = function (username) {
  return {username: username==undefined ? "" : username};
}

if (Meteor.isClient) {

  // Helper to get session variables

  Handlebars.registerHelper('sessionvar',function(v){
    var val = Session.get(v);
    return val;
  });

  Handlebars.registerHelper('origin',function(){
    return window.location.origin
  });

  // Body functions

  Template.body.activetab = function (t) {
    return (t == Session.get("tabs")) || (t == "#review" && !Session.get("tabs"));
  }

  Template.body.username = function () {
    return get_username();
  }

  Template.body.has_username = function () {
    var username = get_username();
    return username != "" && username != undefined;
  }

  Template.body.events({
    'click button#clear': function () {
      var filter = user_filter(get_username());
      Meteor.call("delete_samples", filter);
      set_recording(is_recording()); // update recording timestamp
    }
  });

  // Server timestamp display for debugging purposes

  Template.timestamp.timestamp = function () {
    var ts = get_timestamp();
    if (!ts) return;
    return ts - Date.now();
  };

  var get_timestamp = function () {
    t = Timestamps.findOne();
    if (!t) return 0;
    return t.timestamp;
  }

  Meteor.subscribe("timestamps", 1);

  // Recent samples display

  var timestamps = [];
  var format_sample = function (s) {
    r = sprintf("%4d %d  %6.2f %6.2f %6.2f   %6.1f %6.1f %6.1f",
      s['t']-timestamps[0], s['t'],
      s['x'], s['y'], s['z'],
      s['a'], s['b'], s['c']);
    timestamps[0] = s['t'];
    return r;
  }

  Template.recentdata.recentsamples = function () {
    var s = Samples.findOne(user_filter(get_username()), {sort: {created_at: -1}, limit: 1});
    if (s != null) {
      if (!timestamps.length) {
        timestamps.unshift(s["samples"][0]['t']);
        timestamps.unshift(timestamps[0]);
      } else if (timestamps[0] == s["samples"][19]['t']) {
        timestamps[0] = timestamps[1];
      } else {
      }
      timestamps.unshift(timestamps[0]);
      r = _.map(s["samples"], format_sample );
      timestamps.pop();
      return r;
    } else {
      return [];
    }
  };

  Template.recentdata.time_limit = function () {
    return time_limit;
  }

  // Handle history via location hash

  $(window).bind('popstate', function() {
    Session.set("tabs", location.hash);
  });

  // Navigation tab wiring

  Template.body.rendered = function () {

    // Handler to set tabs session variable whenever tab is shown

    $('#maintabs a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
      Session.set("tabs", e.target.hash);
    });

    // Explicitly show tab content
    // because the Bootstrap data-toggle mechanism doesn't work with Meteor
    // because tab content is not propery rendered before the toggle is executed.
    // Maybe unnecessary with Meteor 0.8 because it does not rebuild the entire DOM.

    if (Session.get("tabs")) {
      $('#maintabs ' + Session.get("tabs") + "tab").tab("show");
    } else {
      $('#maintabs a[data-toggle="tab"]:first').tab("show"); // On startup initalize first tab.
    }
  }

  // Live display of number of samples

  Template.nsamples.nsamples = function () {
    var s = Counts.findOne(); // This can return null during startup!
    if (!s) return "nsamples: Oops!";
    return s.count;
  };

  var is_recording = function () {
    var u = Meteor.user();
    if (!u || !u.profile) return false;
    var ts = Timestamps.findOne();
    if (!ts) return false;
    return ts.timestamp - u.profile.recording < time_limit*1000;
  }

  Template.nsamples.recording = function () {
    return is_recording();
  }

  // Set the recording state

  var set_recording = function(recording) {
    recording_val = recording ? Date.now() : null;
    Meteor.users.update(Meteor.userId(), {$set: {'profile.recording': recording_val}});
  }

  // The recording button

  Template.recording.recording = function () {
    return is_recording();
  }

  Template.recording.events({
    'click button#startstop': function () {
      var recording = is_recording();
      if (!recording && Counts.findOne() && Counts.findOne().count) {
        alert("Please delete samples first.");
        if (Meteor.user().username != "Juan") return;
      }
      set_recording(!recording);
    }
  });

  Template.status.status = function () {
    s = Meteor.status()['status']
    if (s=="connected") {
      r = 'muted' ;
      $('#statusModal').modal('hide');
    } else if (s=="connecting") {
      r = 'text-warning' ;
      $('#statusModal').modal('show');
    } else {
      r = 'text-error';
      s = s + '!'
      $('#statusModal').modal('show');
    }
    if (Meteor.status()['reason']) s += " " + Meteor.status()['reason']
    if (Meteor.status()['retryCount']) s += " (" + Meteor.status()['retryCount'] + ")"
    $('#statusModal #statusModalLabel').html("Server status: " + s + "");
    return new Handlebars.SafeString("" + s + "");
  }

  Deps.autorun(Template.status.status); // Not sure why this one is needed

  // At startup set up device motion event handler.

  Deps.autorun(function () {
    var username = get_username();
    if (!username) return;
    Meteor.subscribe ("counts", username);
  });

  Deps.autorun(function () {
    var username = get_username();
    if (!username) return;
    Meteor.subscribe ("recent_samples", username);
  });

  Meteor.startup(function () {

    Accounts.ui.config({
      passwordSignupFields: 'USERNAME_ONLY'
    });

    var timestamp = 0;
    var samples = [];

    if (window.DeviceMotionEvent != undefined) {

      // Device motion event service routine!

      window.ondevicemotion = function(e) {

        var s = "";

        // Measure sample interval and display on page

        var t = Date.now();
        s += sprintf("Sample period:     %3.0f ms
", (t - timestamp));
        s += sprintf("API sample period: %3.0f ms

", Math.floor(e.interval*1000));
        timestamp = t

        // Create the sample

        var sample = {}
        sample.x = e.accelerationIncludingGravity.x;
        sample.y = e.accelerationIncludingGravity.y;
        sample.z = e.accelerationIncludingGravity.z;
        if ( e.rotationRate ) {
          sample.a = e.rotationRate.alpha;
          sample.b = e.rotationRate.beta;
          sample.c = e.rotationRate.gamma;
        }
        sample.t = t;

        // Live update (Only when not recording)

        if (!is_recording() || Session.get("tabs") != "#collect") {
          samples = [];

          s += sprintf("Timestamp: %f

", sample['t']);
          s += sprintf("Acc:  %6.2f %6.2f %6.2f
", sample['x'], sample['y'], sample['z']);
          if ( e.rotationRate ) {
          s += sprintf("Rot:  %6.1f %6.1f %6.1f
", sample['a'], sample['b'], sample['c']);
          }

          $("#accxyz").html(s);
          return;
        }
        $("#accxyz").html("Collecting...");

        // Every 20 samples save record in mongoDB.

        samples.push(sample);
        if (samples.length >= 20) {
          created_at = t;
          var username = get_username();
          Samples.insert({samples: samples, created_at: created_at, username: username});
          samples = [];
        }
      }
    }
  });
}

// Server

if (Meteor.isServer) {

  Samples.allow({
    insert: function (id, doc) { // Allow user to delete their own documents
      return doc.username == Meteor.user().username;
    }
  });

  var set_recording = function(username, recording) {
    u = Meteor.users.findOne({username: username});
    recording_val = recording ? Date.now() : null;
    Meteor.users.update(u._id, {$set: {'profile.recording': recording_val}});
  };

  var get_recording = function(username, recording) {
    u = Meteor.users.findOne({username: username});
    if (!u) return undefined;
    if (!u.profile) return undefined;
    return u.profile.recording;
  };

  Meteor.methods({
    // Because Meteor does not allow client to delete multiple documents
    delete_samples: function(filter, n) {
      if (n==undefined) {
        Samples.remove(filter);
      } else if (n>0) {
        var ids = Samples.find(filter,{sort: {created_at: 1}, limit: n, fields: {_id: 1}}).fetch();
        ids = ids.map(function(i){return i._id});
        Samples.remove({_id: {'$in': ids}});
        return
      }
    },
  });

  Meteor.publish("counts", function (username) {
    var initializing = true; // Not sure if this is needed. Copied from example.
    var self = this; // Needed to capture value of this for function closures.
    var s = Samples.find(user_filter(username), {fields: {_id: 1}});
    var handle = s.observeChanges({
      added : function (id) {
        if (!initializing) {
          self.changed("counts", username, {count: s.count()});
        }
      },
      removed : function (id) {
        if (!initializing) {
          self.changed("counts", username, {count: s.count()});
        }
      }
    });
    var count = s.count();
    initializing = false;
    self.added("counts", username, {count: count});
    self.ready();
    self.onStop(function () {
      handle.stop();
    });
  });

  Meteor.publish("recent_samples", function (username) {
    var s = Samples.find(user_filter(username), {sort: {created_at: -1}, limit: 1});
    s.rewind();
    return s;
  });

  Meteor.publish("timestamps", function (x) {
    var self = this; // Needed to capture value of this for function closures.
    self.added("timestamps", "0", {timestamp: Date.now()});
    Meteor.setInterval(function (){
      self.changed("timestamps", "0", {timestamp: Date.now()});
    }, 1000)
    self.ready();
  });

  Meteor.startup(function () {
    Samples._ensureIndex({created_at: -1, username: 1})
  });
}

// Server routes

Router.configure({
  notFoundTemplate: 'notFound' // this will render
});

Router.map(function () {
  this.route('body', {
    path: '/'
  });
});

Router.map(function () {
  this.route('json', {
    where: 'server',
    path: '/json/:username',

    action: function () {
      var username = this.params.username;
      // set recording, but not too frequently because changes to user record bog down browser
      var recording = get_recording(username);
      if (!recording || recording && Date.now() > recording + time_limit/2*1000) {
        set_recording(username, Date.now());
      }
      var filter = {username: username};

      var t = this.params.t;
      if (t != undefined) {
        filter['created_at'] = {$gt : parseInt(t)};
      }

      var seconds = 5; // Need at least 2 seconds to allow for occasional catchup
      var skip = 0; // Default skip nothing
      var nrecords = Samples.find(filter).count();
      skip = Math.max(0, nrecords-seconds);

      var samples = Samples.find(filter, {sort: {created_at: 1}, skip: skip});

      // Get samples and form response

      var nrecords_fetched = 0;
      var l = [];
      samples.forEach(function (s) {
        l = l.concat(s.samples);
        nrecords_fetched++;
      });

      this.response.writeHead(200, {'Content-Type': 'application/json', 'Content-Disposition': 'attachment'});
      this.response.end(JSON.stringify(l));

      // Delete samples as we stream them

      var total_records = Samples.find({username: username}).count();
      var ndelete = 0;
      if (total_records > time_limit) ndelete = total_records - time_limit;
//      else if (total_records - nrecords_fetched > 10) ndelete = nrecords_fetched;
      Meteor.call("delete_samples", {username: username}, ndelete);

    }
  });
});
<|endoftext|>


<commit_before>import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { propType } from 'graphql-anywhere';
import { graphql, compose } from 'react-apollo';

import muteQuery from 'react/components/MuteButton/queries/muted';
import mutableFragment from 'react/components/MuteButton/fragments/mutable';
import muteMutation from 'react/components/MuteButton/mutations/mute';
import unmuteMutation from 'react/components/MuteButton/mutations/unmute';

class MuteButton extends Component {
  static propTypes = {
    id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
    type: PropTypes.oneOf(['CONNECTABLE', 'CHANNEL']).isRequired,
    data: PropTypes.shape({
      loading: PropTypes.bool.isRequired,
      mutable: propType(mutableFragment),
    }).isRequired,
    mute: PropTypes.func.isRequired,
    unmute: PropTypes.func.isRequired,
  }

  toggleMute = async () => {
    const {
      id, type, data: { mutable },
    } = this.props;

    const action = mutable.is_muted ? 'unmute' : 'mute';
    const mutation = this.props[action];
    const options = {
      variables: { id, type },
      optimisticResponse: {
        __typename: 'Mutation',
        [action]: {
          __typename: 'MutePayload',
          mutable: {
            ...mutable,
            is_muted: !mutable.is_muted,
          },
        },
      },
    };

    return mutation(options);
  }

  render() {
    const { data: { loading } } = this.props;

    console.log('data', this.props);

    if (loading) return Mute;

    const {
      id, type, mute: _mute, unmute: _unmute,
      data: { mutable },
      ...rest
    } = this.props;

    return (

        {{
          true: 'Unmute',
          false: 'Mute',
        }[mutable.is_muted]}

    );
  }
}

export default compose(
  graphql(muteQuery),
  graphql(muteMutation, { name: 'mute' }),
  graphql(unmuteMutation, { name: 'unmute' }),
)(MuteButton);
<commit_msg>Remove console<commit_after>import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { propType } from 'graphql-anywhere';
import { graphql, compose } from 'react-apollo';

import muteQuery from 'react/components/MuteButton/queries/muted';
import mutableFragment from 'react/components/MuteButton/fragments/mutable';
import muteMutation from 'react/components/MuteButton/mutations/mute';
import unmuteMutation from 'react/components/MuteButton/mutations/unmute';

class MuteButton extends Component {
  static propTypes = {
    id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
    type: PropTypes.oneOf(['CONNECTABLE', 'CHANNEL']).isRequired,
    data: PropTypes.shape({
      loading: PropTypes.bool.isRequired,
      mutable: propType(mutableFragment),
    }).isRequired,
    mute: PropTypes.func.isRequired,
    unmute: PropTypes.func.isRequired,
  }

  toggleMute = async () => {
    const {
      id, type, data: { mutable },
    } = this.props;

    const action = mutable.is_muted ? 'unmute' : 'mute';
    const mutation = this.props[action];
    const options = {
      variables: { id, type },
      optimisticResponse: {
        __typename: 'Mutation',
        [action]: {
          __typename: 'MutePayload',
          mutable: {
            ...mutable,
            is_muted: !mutable.is_muted,
          },
        },
      },
    };

    return mutation(options);
  }

  render() {
    const { data: { loading } } = this.props;

    if (loading) return Mute;

    const {
      id, type, mute: _mute, unmute: _unmute,
      data: { mutable },
      ...rest
    } = this.props;

    return (

        {{
          true: 'Unmute',
          false: 'Mute',
        }[mutable.is_muted]}

    );
  }
}

export default compose(
  graphql(muteQuery),
  graphql(muteMutation, { name: 'mute' }),
  graphql(unmuteMutation, { name: 'unmute' }),
)(MuteButton);
<|endoftext|>


<commit_before><commit_msg>Update video object on load (#28)<commit_after><|endoftext|>


<commit_before>(function($) {
    // Only run this code if browserid is enabled.
    if (!waffle.flag_is_active('browserid')) {
        return;
    }

    $(function() {
        var $globalForm = $('#browserid-form');
        var $form = $globalForm;
        var email = $globalForm.data('email');
        if (email === '') {
            email = null;
        }

        function submitAssertion(assertion) {
            if (assertion) {
                var $next = $form.find('input[name="next"]');
                var next = $next.val();
                var $assertion = $form.find('input[name="assertion"]');

                // If there is no next set yet, make it the current URL.
                if (!next) {
                    $next.val(document.location.pathname + document.location.search);
                }

                $assertion.val(assertion.toString());

                $form.submit();
            }
        }

        $(document).on('click', '.browserid-login', function(e) {
            var $this = $(this);
            var formId = $this.data('form');
            var next;

            e.preventDefault();

            if (formId) {
                $form = $('#' + formId);
            } else {
                $form = $globalForm;
            }

            next = $this.data('next') || document.location.pathname + document.location.search;
            $form.find('input[name="next"]').val(next);

            var originalText = $this.text();
            $this.text(gettext('Signing you in...'));

            navigator.id.request({
                returnTo: next,
                siteName: gettext('Mozilla Support'),
                oncancel: function() {
                    $this.text(originalText);
                }/*,
                TODO: siteLogo: */
            });
        });

        $('a.sign-out').on('click', function(e) {
            e.preventDefault();
            navigator.id.logout();
        });

        navigator.id.watch({
            loggedInUser: email,
            onlogin: submitAssertion,
            onlogout: function() {
                window.location = $form.data('logout-url');
            }
        });

    });
})(jQuery);
<commit_msg>[bug 915086] Add firefox logo to persona popup.<commit_after>(function($) {
    // Only run this code if browserid is enabled.
    if (!waffle.flag_is_active('browserid')) {
        return;
    }

    $(function() {
        var $globalForm = $('#browserid-form');
        var $form = $globalForm;
        var email = $globalForm.data('email');
        if (email === '') {
            email = null;
        }

        function submitAssertion(assertion) {
            if (assertion) {
                var $next = $form.find('input[name="next"]');
                var next = $next.val();
                var $assertion = $form.find('input[name="assertion"]');

                // If there is no next set yet, make it the current URL.
                if (!next) {
                    $next.val(document.location.pathname + document.location.search);
                }

                $assertion.val(assertion.toString());

                $form.submit();
            }
        }

        $(document).on('click', '.browserid-login', function(e) {
            var $this = $(this);
            var formId = $this.data('form');
            var next;

            e.preventDefault();

            if (formId) {
                $form = $('#' + formId);
            } else {
                $form = $globalForm;
            }

            next = $this.data('next') || document.location.pathname + document.location.search;
            $form.find('input[name="next"]').val(next);

            var originalText = $this.text();
            $this.text(gettext('Signing you in...'));

            navigator.id.request({
                returnTo: next,
                siteName: gettext('Mozilla Support'),
                oncancel: function() {
                    $this.text(originalText);
                },
                siteLogo: 'https://support.cdn.mozilla.net/static/img/firefox-256.png'
            });
        });

        $('a.sign-out').on('click', function(e) {
            e.preventDefault();
            navigator.id.logout();
        });

        navigator.id.watch({
            loggedInUser: email,
            onlogin: submitAssertion,
            onlogout: function() {
                window.location = $form.data('logout-url');
            }
        });

    });
})(jQuery);
<|endoftext|>


<commit_before>import React, { PureComponent } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import OutsideClickHandler from 'react-outside-click-handler';
import styled from 'styled-components';
import { position, top, right, bottom, left, width, height } from 'styled-system';
import { debounce } from 'underscore';

import { preset } from 'react/styles/functions';

import compactObject from 'react/util/compactObject';

const Background = styled.div`
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: ${x => x.theme.z.modal};
  pointer-events: none;
`;

const Wrapper = styled.div`
  box-sizing: border-box;
  ${preset(position, { position: 'absolute' })}
  ${top}
  ${right}
  ${bottom}
  ${left}
  ${width}
  ${height}
  pointer-events: all;
`;

export default class Overlay extends PureComponent {
  static propTypes = {
    children: PropTypes.node.isRequired,
    onClose: PropTypes.func.isRequired,
    targetEl: PropTypes.func.isRequired,
    anchorY: PropTypes.oneOf(['top', 'bottom']),
    anchorX: PropTypes.oneOf(['left', 'right']),
    alignToY: PropTypes.oneOf(['top', 'bottom']),
    alignToX: PropTypes.oneOf(['left', 'right']),
    offsetX: PropTypes.number,
    offsetY: PropTypes.number,
    fullWidth: PropTypes.bool,
  }

  static defaultProps = {
    anchorY: 'top',
    anchorX: 'left',
    alignToY: 'bottom',
    alignToX: 'left',
    offsetX: 0,
    offsetY: 0,
    fullWidth: false,
  }

  constructor(props) {
    super(props);

    this.el = document.createElement('div');
  }

  state = {
    top: null,
    right: null,
    bottom: null,
    left: null,
    width: null,
    height: null,
  }

  componentDidMount() {
    document.body.appendChild(this.el);

    this.positionOverlay = () => this.alignToEl(this.props.targetEl());
    this.debouncedPositionOverlay = debounce(this.positionOverlay, 100);

    window.addEventListener('resize', this.debouncedPositionOverlay);
    document.body.addEventListener('mousewheel', this.debouncedPositionOverlay);

    this.positionOverlay();

    // Re-position the overlay if anything in the subtree changes
    this.observer = new MutationObserver(this.positionOverlay);
    this.observer.observe(this.wrapper, { childList: true, subtree: true });
  }

  componentWillUnmount() {
    this.el.parentNode.removeChild(this.el);

    window.removeEventListener('resize', this.debouncedPositionOverlay);
    document.body.removeEventListener('mousewheel', this.debouncedPositionOverlay);

    this.observer.disconnect();
  }

  alignToEl = (el) => {
    const {
      anchorY, anchorX, alignToY, alignToX, offsetY, offsetX, fullWidth,
    } = this.props;

    const { [alignToY]: y, [alignToX]: x, width: elWidth } = el.getBoundingClientRect();

    const positions = {
      top: y,
      bottom: window.innerHeight - y,
      left: x,
      right: window.innerWidth - x,
    };

    const positionState = {
      [anchorY]: positions[anchorY] + offsetY,
      [anchorX]: positions[anchorX] + offsetX,
    };

    if (fullWidth) {
      positionState.width = elWidth;
    }

    // Moves into position
    this.setState(positionState, () => {
      // Then handle overflowing calculations
      const {
        bottom: bottomEdge,
        top: topEdge,
        height: wrapperHeight,
      } = this.wrapper.getBoundingClientRect();

      const isOverflowingViewportTop = anchorY === 'bottom' && topEdge <= 0;
      const isOverflowingViewportBottom = anchorY === 'top' && bottomEdge >= window.innerHeight;

      if (isOverflowingViewportTop) {
        return this.setState({ height: wrapperHeight + topEdge, topEdge });
      }

      if (isOverflowingViewportBottom) {
        const bottomDifference = window.innerHeight - bottomEdge;
        return this.setState({ height: wrapperHeight + bottomDifference });
      }

      return null;
    });
  }

  render() {
    const { children, onClose, ...rest } = this.props;

    return ReactDOM.createPortal(
      (
        <Background>
          <OutsideClickHandler onOutsideClick={onClose}>
            <Wrapper
              {...compactObject(this.state)}
              {...rest}
              innerRef={(el) => { this.wrapper = el; }}
            >
              {children}
            </Wrapper>
          </OutsideClickHandler>
        </Background>
      ), this.el,
    );
  }
}
<commit_msg>Try to do the right thing WRT container height<commit_after>import React, { PureComponent } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import OutsideClickHandler from 'react-outside-click-handler';
import styled from 'styled-components';
import { position, top, right, bottom, left, width, height } from 'styled-system';
import { debounce } from 'underscore';

import { preset } from 'react/styles/functions';

import compactObject from 'react/util/compactObject';

const Background = styled.div`
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: ${x => x.theme.z.modal};
  pointer-events: none;
`;

const Wrapper = styled.div`
  box-sizing: border-box;
  ${preset(position, { position: 'absolute' })}
  ${top}
  ${right}
  ${bottom}
  ${left}
  ${width}
  ${height}
  pointer-events: all;
`;

export default class Overlay extends PureComponent {
  static propTypes = {
    children: PropTypes.node.isRequired,
    onClose: PropTypes.func.isRequired,
    targetEl: PropTypes.func.isRequired,
    anchorY: PropTypes.oneOf(['top', 'bottom']),
    anchorX: PropTypes.oneOf(['left', 'right']),
    alignToY: PropTypes.oneOf(['top', 'bottom']),
    alignToX: PropTypes.oneOf(['left', 'right']),
    offsetX: PropTypes.number,
    offsetY: PropTypes.number,
    fullWidth: PropTypes.bool,
  }

  static defaultProps = {
    anchorY: 'top',
    anchorX: 'left',
    alignToY: 'bottom',
    alignToX: 'left',
    offsetX: 0,
    offsetY: 0,
    fullWidth: false,
  }

  constructor(props) {
    super(props);

    this.el = document.createElement('div');
  }

  state = {
    top: null,
    right: null,
    bottom: null,
    left: null,
    width: null,
    height: null,
  }

  componentDidMount() {
    document.body.appendChild(this.el);

    this.positionOverlay = () => this.alignToEl(this.props.targetEl());
    this.debouncedPositionOverlay = debounce(this.positionOverlay, 100);

    window.addEventListener('resize', this.debouncedPositionOverlay);
    document.body.addEventListener('mousewheel', this.debouncedPositionOverlay);

    this.positionOverlay();

    // Re-position the overlay if anything in the subtree changes
    this.observer = new MutationObserver(this.positionOverlay);
    this.observer.observe(this.wrapper, { childList: true, subtree: true });
  }

  componentWillUnmount() {
    this.el.parentNode.removeChild(this.el);

    window.removeEventListener('resize', this.debouncedPositionOverlay);
    document.body.removeEventListener('mousewheel', this.debouncedPositionOverlay);

    this.observer.disconnect();
  }

  alignToEl = (el) => {
    const {
      anchorY, anchorX, alignToY, alignToX, offsetY, offsetX, fullWidth,
    } = this.props;

    const { [alignToY]: y, [alignToX]: x, width: elWidth } = el.getBoundingClientRect();

    const positions = {
      top: y,
      bottom: window.innerHeight - y,
      left: x,
      right: window.innerWidth - x,
    };

    const positionState = {
      [anchorY]: positions[anchorY] + offsetY,
      [anchorX]: positions[anchorX] + offsetX,
    };

    if (fullWidth) {
      positionState.width = elWidth;
    }

    // Moves into position
    this.setState(positionState, () => {
      // Then handle overflowing calculations
      const {
        bottom: bottomEdge,
        top: topEdge,
        height: wrapperHeight,
      } = this.wrapper.getBoundingClientRect();

      const isOverflowingViewportTop = anchorY === 'bottom' && topEdge <= 0;
      const isOverflowingViewportBottom = anchorY === 'top' && bottomEdge >= window.innerHeight;

      if (isOverflowingViewportTop) {
        return this.setState({ height: wrapperHeight + topEdge, topEdge });
      }

      if (isOverflowingViewportBottom) {
        const bottomDifference = window.innerHeight - bottomEdge;
        return this.setState({ height: wrapperHeight + bottomDifference });
      }

      if (this.state.height) {
        return this.setState({ height: null });
      }

      return null;
    });
  }

  render() {
    const { children, onClose, ...rest } = this.props;

    return ReactDOM.createPortal(
      (
        <Background>
          <OutsideClickHandler onOutsideClick={onClose}>
            <Wrapper
              {...compactObject(this.state)}
              {...rest}
              innerRef={(el) => { this.wrapper = el; }}
            >
              {children}
            </Wrapper>
          </OutsideClickHandler>
        </Background>
      ), this.el,
    );
  }
}
<|endoftext|>


<commit_before>/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/*
 * Copyright 2013 Mozilla Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*global Multiname, executeActions */

var SpriteDefinition = (function () {
  var def = {
    __class__: 'flash.display.Sprite',

    initialize: function () {
      this._buttonMode = false;
      this._hitArea = null;
      this._useHandCursor = true;
      this._hitTarget = null;
      this._depthMap = { };

      var s = this.symbol;
      if (s) {
        this._graphics = s.graphics || new flash.display.Graphics();

        if (s.timeline) {
          var displayList = s.timeline[0];
          if (displayList) {
            var depths = displayList.depths;
            for (var i = 0; i < depths.length; i++) {
              var cmd = displayList[depths[i]];
              if (cmd) {
                this._addTimelineChild(cmd);
              }
            }
          }
        }
      } else {
        this._graphics = new flash.display.Graphics();
      }
      this._graphics._parent = this;
    },

    _addTimelineChild: function addTimelineChild(cmd, index) {
      var symbolPromise = cmd.promise;
      var symbolInfo = symbolPromise.value;
      var props = Object.create(symbolInfo.props);

      props.depth = cmd.depth;
      props.symbolId = cmd.symbolId;

      if (cmd.clip) {
        props.clipDepth = cmd.clipDepth;
      }
      if (cmd.hasCxform) {
        props.cxform = cmd.cxform;
      }
      if (cmd.hasMatrix) {
        props.currentTransform = cmd.matrix;
      }
      if (cmd.hasName) {
        props.name = cmd.name;
      }
      if (cmd.hasRatio) {
        props.ratio = cmd.ratio / 0xffff;
      }
      if (cmd.blend) {
        props.blendMode = cmd.blendMode;
      }

      var child = {
        className: symbolInfo.className,
        events: cmd.events,
        props: props
      };

      if (index !== undefined) {
        this._children.splice(index, 0, child);
      } else {
        this._children.push(child);
      }

      this._sparse = true;
    },
    _removeTimelineChild: function removeTimelineChild(cmd) {
      var child = this._depthMap[cmd.depth];

      if (!child) {
        return;
      }

      this._depthMap[child._depth] = null;
      child._depth = null;

      if (!child._owned) {
        return;
      }

      this._sparse = true;
      this.removeChild(child);

      child.destroy();
      if (child._isPlaying) {
        child.stop();
      }
    },
    _constructChildren: function () {
      if (!this._sparse) {
        return;
      }

      var loader = this._loader;

      var children = this._children;
      for (var i = 0; i < children.length; i++) {
        var symbolInfo = children[i];

        if (flash.display.DisplayObject.class.isInstanceOf(symbolInfo)) {
          symbolInfo._index = i;
        } else {
          // HACK application domain may have the symbol class --
          // checking which domain has a symbol class
          var symbolClass = avm2.systemDomain.findClass(symbolInfo.className) ?
            avm2.systemDomain.getClass(symbolInfo.className) :
            avm2.applicationDomain.getClass(symbolInfo.className);

          var props = Object.create(symbolInfo.props);
          var name = props.name;

          props.animated = true;
          props.owned = true;
          props.parent = this;
          props.stage = this._stage;

          if (this._level > -1) {
            props.level = this._level + 1;
          }

          props.index = i;

          var instance = symbolClass.createAsSymbol(props);

          // If we bound the instance to a name, set it.
          //
          // XXX: I think this always has to be a trait.
          if (name) {
            this[Multiname.getPublicQualifiedName(name)] = instance;
          }

          // Call the constructor now that we've made the symbol instance,
          // instantiated all its children, and set the display list-specific
          // properties.
          //
          // XXX: I think we're supposed to throw if the symbol class
          // constructor is not nullary.
          symbolClass.instanceConstructor.call(instance);

          if (flash.display.BitmapData.class.isInstanceOf(instance)) {
            var bitmapData = instance;
            instance = flash.display.Bitmap.class.createAsSymbol(props);
            flash.display.Bitmap.class.instanceConstructor.call(instance, bitmapData);
          }

          if (!loader._isAvm2Enabled) {
            this._initAvm1Bindings(instance, name, symbolInfo.events);
            instance._dispatchEvent("init");
            instance._dispatchEvent("construct");
          }

          instance._dispatchEvent("load");
          instance._dispatchEvent("added");
          if (this._stage) {
            this._stage._addToStage(instance);
          }

          children[i] = instance;

          this._depthMap[instance._depth] = instance;
        }
      }

      this._sparse = false;
    },

    _duplicate: function (name, depth, initObject) {
      // TODO proper child cloning, initObject and display list insertion
      // for now just created symbol based on previous timeline information
      // (code is borrowed from the _constructChildren)

      var loader = this._loader;
      var parent = this._parent;
      var children = parent._children;
      var symbolClass = this.class;
      var symbolInfo = this.symbol;

      var props = Object.create(symbolInfo);
      props.name = name;
      props.depth = depth;
      props.parent = parent;

      var instance = symbolClass.createAsSymbol(props);
      if (name)
        parent[Multiname.getPublicQualifiedName(name)] = instance;

      symbolClass.instanceConstructor.call(instance);

      if (!loader._isAvm2Enabled) {
        parent._initAvm1Bindings(instance, name, symbolInfo && symbolInfo.events);
        instance._dispatchEvent("init");
        instance._dispatchEvent("construct");
      }

      instance._dispatchEvent("load");
      instance._dispatchEvent("added");
      if (this._stage) {
        instance._invalidate();
      }

      children.push(instance);

      return instance;
    },
    _insertChildAtDepth: function (child, depth) {
      // TODO insert with specific depth
      this.addChild(child);
      var name = child._name;
      if (name && !this._loader._isAvm2Enabled) {
        this._getAS2Object().asSetPublicProperty(name, child._getAS2Object());
      }
    },
    _initAvm1Bindings: function (instance, name, events) {
      var loader = this._loader;
      var avm1Context = loader._avm1Context;
      var symbolProps = instance.symbol;

      if (symbolProps && symbolProps.variableName) {
        var variableName = symbolProps.variableName;
        var hasPath = variableName.lastIndexOf('.') >= 0 ||
                      variableName.lastIndexOf(':') >= 0;
        var clip;
        if (hasPath) {
          var targetPath = variableName.split(/[.:\/]/g);
          variableName = targetPath.pop();
          if (targetPath[0] == '_root' || targetPath[0] === '') {
            clip = this.root._getAS2Object();
            targetPath.shift();
            if (targetPath[0] === '') {
              targetPath.shift();
            }
          } else {
            clip = instance._getAS2Object();
          }
          while (targetPath.length > 0) {
            var childName = targetPath.shift();
            clip = clip.asGetPublicProperty(childName) || clip[childName];
            if (!clip) {
              throw new Error('Cannot find ' + childName + ' variable');
            }
          }
        } else
          clip = instance._getAS2Object();
        if (!clip.asHasProperty(undefined, variableName, 0)) {
          clip.asSetPublicProperty(variableName, instance.text);
        }
        instance._addEventListener('advanceFrame', function() {
          instance.text = clip.asGetPublicProperty(variableName);
        });
      }

      if (events) {
        var eventsBound = [];
        for (var i = 0; i < events.length; i++) {
          var event = events[i];
          if (event.eoe) {
            break;
          }
          /*jshint -W083 */
          var fn = function(actionBlock) {
            return executeActions(actionBlock, avm1Context, this._getAS2Object());
          }.bind(instance, event.actionsData);
          for (var eventName in event) {
            if (eventName.indexOf("on") !== 0 || !event[eventName])
              continue;
            var avm2EventName = eventName[2].toLowerCase() + eventName.substring(3);
            if (avm2EventName === 'enterFrame') {
              avm2EventName = 'frameConstructed';
            }
            instance._addEventListener(avm2EventName, fn, false);
            eventsBound.push({name: avm2EventName, fn: fn});
          }
        }
        if (eventsBound.length > 0) {
          instance._addEventListener('removed', function (eventsBound) {
            for (var i = 0; i < eventsBound.length; i++) {
              instance._removeEventListener(eventsBound[i].name, eventsBound[i].fn, false);
            }
          }.bind(instance, eventsBound), false);
        }
      }

      if (name) {
        this._getAS2Object().asSetPublicProperty(name, instance._getAS2Object());
      }
    },

    _gotoButtonState: function gotoButtonState(stateName) {
      // does nothing for sprite?
    },

    get buttonMode() {
      return this._buttonMode;
    },
    set buttonMode(val) {
      this._buttonMode = val;
    },
    get graphics() {
      return this._graphics;
    },
    get hitArea() {
      return this._hitArea;
    },
    set hitArea(val) {
      if (this._hitArea === val) {
        return;
      }

      if (val && val._hitTarget) {
        val._hitTarget.hitArea = null;
      }

      this._hitArea = val;
      if (val) {
        val._hitTarget = this;
      }
    },
    get soundTransform() {
      notImplemented();
    },
    set soundTransform(val) {
      notImplemented();
    },
    get useHandCursor() {
      return this._useHandCursor;
    },
    set useHandCursor(val) {
      this._useHandCursor = val;
      if (this._stage) {
        this._stage._syncCursor();
      }
    },

    startDrag: function (lockCenter, bounds) {
      notImplemented();
    },
    startTouchDrag: function (touchPointID, lockCenter, bounds) {
      notImplemented();
    },
    stopDrag: function () {
      notImplemented();
    },
    stopTouchDrag: function (touchPointID) {
      notImplemented();
    }
  };

  var desc = Object.getOwnPropertyDescriptor;

  def.__glue__ = {
    native: {
      instance: {
        graphics: desc(def, "graphics"),
        buttonMode: desc(def, "buttonMode"),
        dropTarget: desc(def, "dropTarget"),
        startDrag: def.startDrag,
        stopDrag: def.stopDrag,
        startTouchDrag: def.startTouchDrag,
        stopTouchDrag: def.stopTouchDrag,
        constructChildren: def._constructChildren,
        hitArea: desc(def, "hitArea"),
        useHandCursor: desc(def, "useHandCursor"),
        soundTransform: desc(def, "soundTransform")
      }
    }
  };

  return def;
}).call(this);
<commit_msg>Avoids overriding existing movie var<commit_after>/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/*
 * Copyright 2013 Mozilla Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*global Multiname, executeActions */

var SpriteDefinition = (function () {
  var def = {
    __class__: 'flash.display.Sprite',

    initialize: function () {
      this._buttonMode = false;
      this._hitArea = null;
      this._useHandCursor = true;
      this._hitTarget = null;
      this._depthMap = { };

      var s = this.symbol;
      if (s) {
        this._graphics = s.graphics || new flash.display.Graphics();

        if (s.timeline) {
          var displayList = s.timeline[0];
          if (displayList) {
            var depths = displayList.depths;
            for (var i = 0; i < depths.length; i++) {
              var cmd = displayList[depths[i]];
              if (cmd) {
                this._addTimelineChild(cmd);
              }
            }
          }
        }
      } else {
        this._graphics = new flash.display.Graphics();
      }
      this._graphics._parent = this;
    },

    _addTimelineChild: function addTimelineChild(cmd, index) {
      var symbolPromise = cmd.promise;
      var symbolInfo = symbolPromise.value;
      var props = Object.create(symbolInfo.props);

      props.depth = cmd.depth;
      props.symbolId = cmd.symbolId;

      if (cmd.clip) {
        props.clipDepth = cmd.clipDepth;
      }
      if (cmd.hasCxform) {
        props.cxform = cmd.cxform;
      }
      if (cmd.hasMatrix) {
        props.currentTransform = cmd.matrix;
      }
      if (cmd.hasName) {
        props.name = cmd.name;
      }
      if (cmd.hasRatio) {
        props.ratio = cmd.ratio / 0xffff;
      }
      if (cmd.blend) {
        props.blendMode = cmd.blendMode;
      }

      var child = {
        className: symbolInfo.className,
        events: cmd.events,
        props: props
      };

      if (index !== undefined) {
        this._children.splice(index, 0, child);
      } else {
        this._children.push(child);
      }

      this._sparse = true;
    },
    _removeTimelineChild: function removeTimelineChild(cmd) {
      var child = this._depthMap[cmd.depth];

      if (!child) {
        return;
      }

      this._depthMap[child._depth] = null;
      child._depth = null;

      if (!child._owned) {
        return;
      }

      this._sparse = true;
      this.removeChild(child);

      child.destroy();
      if (child._isPlaying) {
        child.stop();
      }
    },
    _constructChildren: function () {
      if (!this._sparse) {
        return;
      }

      var loader = this._loader;

      var children = this._children;
      for (var i = 0; i < children.length; i++) {
        var symbolInfo = children[i];

        if (flash.display.DisplayObject.class.isInstanceOf(symbolInfo)) {
          symbolInfo._index = i;
        } else {
          // HACK application domain may have the symbol class --
          // checking which domain has a symbol class
          var symbolClass = avm2.systemDomain.findClass(symbolInfo.className) ?
            avm2.systemDomain.getClass(symbolInfo.className) :
            avm2.applicationDomain.getClass(symbolInfo.className);

          var props = Object.create(symbolInfo.props);
          var name = props.name;

          props.animated = true;
          props.owned = true;
          props.parent = this;
          props.stage = this._stage;

          if (this._level > -1) {
            props.level = this._level + 1;
          }

          props.index = i;

          var instance = symbolClass.createAsSymbol(props);

          // If we bound the instance to a name, set it.
          //
          // XXX: I think this always has to be a trait.
          if (name) {
            this[Multiname.getPublicQualifiedName(name)] = instance;
          }

          // Call the constructor now that we've made the symbol instance,
          // instantiated all its children, and set the display list-specific
          // properties.
          //
          // XXX: I think we're supposed to throw if the symbol class
          // constructor is not nullary.
          symbolClass.instanceConstructor.call(instance);

          if (flash.display.BitmapData.class.isInstanceOf(instance)) {
            var bitmapData = instance;
            instance = flash.display.Bitmap.class.createAsSymbol(props);
            flash.display.Bitmap.class.instanceConstructor.call(instance, bitmapData);
          }

          if (!loader._isAvm2Enabled) {
            this._initAvm1Bindings(instance, name, symbolInfo.events);
            instance._dispatchEvent("init");
            instance._dispatchEvent("construct");
          }

          instance._dispatchEvent("load");
          instance._dispatchEvent("added");
          if (this._stage) {
            this._stage._addToStage(instance);
          }

          children[i] = instance;

          this._depthMap[instance._depth] = instance;
        }
      }

      this._sparse = false;
    },

    _duplicate: function (name, depth, initObject) {
      // TODO proper child cloning, initObject and display list insertion
      // for now just created symbol based on previous timeline information
      // (code is borrowed from the _constructChildren)

      var loader = this._loader;
      var parent = this._parent;
      var children = parent._children;
      var symbolClass = this.class;
      var symbolInfo = this.symbol;

      var props = Object.create(symbolInfo);
      props.name = name;
      props.depth = depth;
      props.parent = parent;

      var instance = symbolClass.createAsSymbol(props);
      if (name)
        parent[Multiname.getPublicQualifiedName(name)] = instance;

      symbolClass.instanceConstructor.call(instance);

      if (!loader._isAvm2Enabled) {
        parent._initAvm1Bindings(instance, name, symbolInfo && symbolInfo.events);
        instance._dispatchEvent("init");
        instance._dispatchEvent("construct");
      }

      instance._dispatchEvent("load");
      instance._dispatchEvent("added");
      if (this._stage) {
        instance._invalidate();
      }

      children.push(instance);

      return instance;
    },
    _insertChildAtDepth: function (child, depth) {
      // TODO insert with specific depth
      this.addChild(child);
      var name = child._name;
      var loader = this._loader;
      // HACK attempting to add movie clip as a variable
      if (name && loader && !loader._isAvm2Enabled &&
          !this._getAS2Object().asHasProperty(undefined, name, 0, true)) {
        this._getAS2Object().asSetPublicProperty(name, child._getAS2Object());
      }
    },
    _initAvm1Bindings: function (instance, name, events) {
      var loader = this._loader;
      var avm1Context = loader._avm1Context;
      var symbolProps = instance.symbol;

      if (symbolProps && symbolProps.variableName) {
        var variableName = symbolProps.variableName;
        var hasPath = variableName.lastIndexOf('.') >= 0 ||
                      variableName.lastIndexOf(':') >= 0;
        var clip;
        if (hasPath) {
          var targetPath = variableName.split(/[.:\/]/g);
          variableName = targetPath.pop();
          if (targetPath[0] == '_root' || targetPath[0] === '') {
            clip = this.root._getAS2Object();
            targetPath.shift();
            if (targetPath[0] === '') {
              targetPath.shift();
            }
          } else {
            clip = instance._getAS2Object();
          }
          while (targetPath.length > 0) {
            var childName = targetPath.shift();
            clip = clip.asGetPublicProperty(childName) || clip[childName];
            if (!clip) {
              throw new Error('Cannot find ' + childName + ' variable');
            }
          }
        } else
          clip = instance._getAS2Object();
        if (!clip.asHasProperty(undefined, variableName, 0)) {
          clip.asSetPublicProperty(variableName, instance.text);
        }
        instance._addEventListener('advanceFrame', function() {
          instance.text = clip.asGetPublicProperty(variableName);
        });
      }

      if (events) {
        var eventsBound = [];
        for (var i = 0; i < events.length; i++) {
          var event = events[i];
          if (event.eoe) {
            break;
          }
          /*jshint -W083 */
          var fn = function(actionBlock) {
            return executeActions(actionBlock, avm1Context, this._getAS2Object());
          }.bind(instance, event.actionsData);
          for (var eventName in event) {
            if (eventName.indexOf("on") !== 0 || !event[eventName])
              continue;
            var avm2EventName = eventName[2].toLowerCase() + eventName.substring(3);
            if (avm2EventName === 'enterFrame') {
              avm2EventName = 'frameConstructed';
            }
            instance._addEventListener(avm2EventName, fn, false);
            eventsBound.push({name: avm2EventName, fn: fn});
          }
        }
        if (eventsBound.length > 0) {
          instance._addEventListener('removed', function (eventsBound) {
            for (var i = 0; i < eventsBound.length; i++) {
              instance._removeEventListener(eventsBound[i].name, eventsBound[i].fn, false);
            }
          }.bind(instance, eventsBound), false);
        }
      }

      if (name) {
        this._getAS2Object().asSetPublicProperty(name, instance._getAS2Object());
      }
    },

    _gotoButtonState: function gotoButtonState(stateName) {
      // does nothing for sprite?
    },

    get buttonMode() {
      return this._buttonMode;
    },
    set buttonMode(val) {
      this._buttonMode = val;
    },
    get graphics() {
      return this._graphics;
    },
    get hitArea() {
      return this._hitArea;
    },
    set hitArea(val) {
      if (this._hitArea === val) {
        return;
      }

      if (val && val._hitTarget) {
        val._hitTarget.hitArea = null;
      }

      this._hitArea = val;
      if (val) {
        val._hitTarget = this;
      }
    },
    get soundTransform() {
      notImplemented();
    },
    set soundTransform(val) {
      notImplemented();
    },
    get useHandCursor() {
      return this._useHandCursor;
    },
    set useHandCursor(val) {
      this._useHandCursor = val;
      if (this._stage) {
        this._stage._syncCursor();
      }
    },

    startDrag: function (lockCenter, bounds) {
      notImplemented();
    },
    startTouchDrag: function (touchPointID, lockCenter, bounds) {
      notImplemented();
    },
    stopDrag: function () {
      notImplemented();
    },
    stopTouchDrag: function (touchPointID) {
      notImplemented();
    }
  };

  var desc = Object.getOwnPropertyDescriptor;

  def.__glue__ = {
    native: {
      instance: {
        graphics: desc(def, "graphics"),
        buttonMode: desc(def, "buttonMode"),
        dropTarget: desc(def, "dropTarget"),
        startDrag: def.startDrag,
        stopDrag: def.stopDrag,
        startTouchDrag: def.startTouchDrag,
        stopTouchDrag: def.stopTouchDrag,
        constructChildren: def._constructChildren,
        hitArea: desc(def, "hitArea"),
        useHandCursor: desc(def, "useHandCursor"),
        soundTransform: desc(def, "soundTransform")
      }
    }
  };

  return def;
}).call(this);
<|endoftext|>


<commit_before>(function(exports) {
    function round(num, digits) {
        digits = digits || 0;
        return Math.round(num * Math.pow(10, digits)) / Math.pow(10, digits);
    }

    var Color = function(arr) {
        if (typeof(arr) === "string") {
            arr = arr.split(",");
        }

        this.r = arr[0];
        this.g = arr[1];
        this.b = arr[2];
        if (arr.length === 4) {
            this.a = round(arr[3] / 255, 2);
        }
        if (typeof(this.r) === 'undefined' || typeof(this.g) === 'undefined' || typeof(this.b) === 'undefined') {
            throw "Invalid Color Array passed in";
        }
    };

    Color.getAvg = function(a, b) {
        var color = [
            (a.r + b.r) / 2,
            (a.g + b.g) / 2,
            (a.b + b.b) / 2
        ];
        if ((typeof(a.a) !== 'undefined') || typeof(b.a !== 'undefined')) {
            a.a = (typeof a.a === 'undefined') ? 255 : a.a;
            b.a = (typeof b.a === 'undefined') ? 255 : b.a;
            color.push((a.a + b.a) / 2);
        }

        return new Color(color);
    };

    Color.prototype.equals = function(b, tolerance) {
        tolerance = (typeof(tolerance) === "undefined") ? 4 : tolerance;
        if (Math.abs(this.r - b.r) > tolerance) {
            return false;
        }
        if (Math.abs(this.g - b.g) > tolerance) {
            return false;
        }
        if (Math.abs(this.b - b.b) > tolerance) {
            return false;
        }
        // compare alphas -- treat undefined alpha as opaque
        var aAlpha = (typeof(this.a) === 'undefined') ? 1 : this.a;
        var bAlpha = (typeof(b.a) === 'undefined') ? 1 : b.a;
        var aTolerance = tolerance / 25;
        if (Math.abs(aAlpha - bAlpha) > tolerance) {
            return false;
        }

        return true;
    };

    Color.prototype.distanceFrom = function(color2) {
        // http://www.compuphase.com/cmetric.htm
        return Math.sqrt(
            3 * Math.pow(this.r - color2.r, 2) + 4 * Math.pow(this.g - color2.g, 2) + 2 * Math.pow(this.b - color2.b, 2) + Math.pow(this.a - color2.a, 2)
        );
    };

    Color.prototype.toString = function() {
        return (this.a === 1) ?
          "rgb("  + round(this.r) + ", " + round(this.g) + ", " + round(this.b) + ")" :
              "rgba(" + round(this.r) + ", " + round(this.g) + ", " + round(this.b) + ", " + round(this.a, 2) + ")";
    };

    function Gradient(stops, angle, length) {
        this.stops = stops;
        this.angle = angle;
    }

    Gradient.prototype.toCss = function() {
        if (this.stops.length === 1) {
            return "background-color: " + this.stops[0].color.toString();
       }

        var stops = this.stops.map(function(s) {
            return s.color.toString() + " " + round(s.idx * 100) + "%";
        });

        var css = this.angle + "deg, " + stops.join(",");
        return "background: -webkit-linear-gradient(" + css + ");\n" +
                "background: -o-linear-gradient(" + css + ");\n" +
                "background: -ms-linear-gradient(" + css + ");\n" +
                "background: -moz-linear-gradient(" + css + ");\n" +
                "background: linear-gradient(" + css + ");\n";
    };

    function getPixel(arr, x, y) {
        if (y === undefined) {
            return new Color(arr[x]);
        }

        return new Color(arr[y][x]);
    }

    // get the middle color of a 1 dimensional array
    function getMid(arr, start, end) {
        if (arr.length === 1) {
            return new Color(arr[0]);
        }

        start = start || 0;
        end = (end || arr.length) - start;
        arr = arr.slice(start);

        if (end % 2 === 0) {
            return getPixel(arr, end / 2);
        }
        else {
            return Color.getAvg(getPixel(arr, Math.floor(end / 2)), getPixel(arr, Math.ceil(end / 2)));
        }
    }

    function vectorToLine(angle, length) {
        function pointOfAngle(a) {
            function toRads(d) {
                return (d * Math.PI) / 180;
            }
            return {
                x: Math.max(Math.cos(toRads(a)), 0),
                y: Math.max(Math.sin(toRads(a)), 0)
            };
        }

        angle = angle % 360;
        var startPoint = pointOfAngle(180 - angle);
        var endPoint = pointOfAngle(0 - angle);

        return {
            x1: startPoint.x * length,
            y1: startPoint.y * length,
            x2: endPoint.x * length,
            y2: endPoint.y * length
        };
    }

    // returns unique values from a sorted array
    function uniqueArray(arr) {
        var ret = [];
        var last;

        for (var i = 0; i < arr.length; i++) {
            if (arr[i] !== last) {
                ret.push(arr[i]);
                last = arr[i];
            }
        }
        return ret;
    }

    function getAngle(arr) {
        function getSingleColorAngles(array) {
            var ret = [];
            for (var angle = 90; angle <= 269; angle++) {
                // check to see if the entire array is the same color
                var singlearr = getSingleDimensionalArray(array, angle).map(function(a) {
                    return a.join();// convert colors to strings (sorting multidimensional arrays is SLOW)
                });
                // simple case -- the arrays are 100% identical
                if (singlearr[0] === singlearr[singlearr.length - 1]) {
                    ret.push(angle - 90);
                }
                else {
                    // slow case -- compare all unique color values with a threshold
                    var uniquevals = uniqueArray(singlearr);
                    var first = uniquevals[0];
                    for (var i = 1; i < uniquevals.length; i++) {
                        if (!colorsEqual(first, uniquevals[i])) {
                            break;
                        }
                        else {
                            if (i == uniquevals.length - 1) {
                                ret.push(angle - 90);
                            }
                        }
                    }
                }
            }
            return ret;
        }

        function getLikely(array, possibles) {
            var sorted = possibles.map(function(angle) {
                var grad = getSingleDimensionalArray(array, angle);
                return {
                    angle: angle,
                    stops: getStops(grad)
                };
            });
            sorted = sorted.sort(function(a, b) {
                if (a.stops.length === b.stops.length) {
                    // for same # of stops, we want the angle that provides the most difference in colors
                    return a.angle - b.angle;
                }
                // single stops are an edge case, so de-prioritize them
                if (a.stops.length === 1) {
                    return 1;
                }
                if (b.stops.length === 1) {
                    return -1;
                }

                return a.stops.length - b.stops.length;
            });

            return sorted[0].angle;
        }

        var possibles = getSingleColorAngles(arr);
        var angle = getLikely(arr, possibles);
        if (typeof(angle) === "undefined") {
            throw "Couldn't find a gradient angle";
        }

        return angle;
    }

    function getSingleDimensionalArray(arr, angle) {
        var width = arr[0].length;
        var height = arr.length;
        var multiplier = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2));
        var vector = vectorToLine(angle, multiplier);
        var coords = bresenhamLine(round(vector.x1), round(vector.y1), round(vector.x2), round(vector.y2));

        var ret = [];
        for (var i = 0; i < coords.length; i++) {
            var x = coords[i][0];
            var y = coords[i][1];

            if (typeof(arr[x]) !== "undefined" && typeof(arr[x][y]) !== "undefined") {
                ret.push(arr[x][y]);
            }
        }

        return ret;
    }

    // http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
    function bresenhamLine(x1, y1, x2, y2) {
        if (isNaN(x1) || isNaN(y1) || isNaN(x2) || isNaN(y2)) {
            throw "Invalid coordinates for Bresenham's";
        }

        x1 = Math.round(x1);
        y1 = Math.round(y1);
        x2 = Math.round(x2);
        y2 = Math.round(y2);

        var coords = [];
        var dx = Math.abs(x2 - x1);
        var dy = Math.abs(y2 - y1);
        var sx = (x1 < x2) ? 1 : -1;
        var sy = (y1 < y2) ? 1 : -1;
        var err = dx - dy;

        coords.push([x1, y1]);
        while (!((x1 === x2) && (y1 === y2))) {
            var e2 = err * 2;
            if (e2 > -dy) {
                err -= dy;
                x1 += sx;
            }
            if (e2 < dx) {
                err += dx;
                y1 += sy;
            }
            // Set coordinates
            coords.push([x1, y1]);
        }

        return coords;
    }

    function getStops(arr, start, end) {
        var ret = [];
        start = start || 0;
        end = end || arr.length - 1;

        var startColor = getPixel(arr, start);
        var endColor = getPixel(arr, end);
        var average = Color.getAvg(startColor, endColor);
        var mid = getMid(arr, start, end);

        // we found a stop -- add it
        if (average.equals(mid)) {
            ret.push(start);

            // we're not at the end -- search between STOP and ARR.LEN for more stops
            if (end !== arr.length - 1) {
                ret = ret.concat(getStops(arr, end, arr.length - 1));
            }
            // found a stop at the end of the array -- break out of the loop
            else if (end === arr.length - 1 && !startColor.equals(endColor)) {
                ret.push(end);
            }
        }
        // no stop found -- try again
        else {
            ret = ret.concat(getStops(arr, start, end - 1));
        }

        return ret;
    }

    function calculateGradient(arr) {
        var angle = getAngle(arr);
        var lineArray = getSingleDimensionalArray(arr, angle);
        var stops = getStops(lineArray);
        var ret = stops.map(function(s) {
            var idx = round(s / (lineArray.length - 1), 2);
            return {
                idx: idx,
                color: getPixel(lineArray, s)
            };
        });

        return new Gradient(ret, angle - 90);
    }

    function getColorArray(ctx) {
        var imageData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
        var width = imageData.width;
        var height = imageData.height;
        var data = imageData.data;

        var colors = [];
        for (var y = 0; y < height; y++) {
            var row = [];
            for (var x = 0; x < width; x++) {
                var idx = ((width * y) + x) * 4;
                var r = data[idx];
                var g = data[idx + 1];
                var b = data[idx + 2];
                var a = data[idx + 3];
                row.push([r, g, b, a]);
            }

            colors[y] = row;
        }

        return colors;
    }

    function findGradFromCanvas(canvas) {
        var ctx = canvas.getContext("2d");
        var colors = getColorArray(ctx);
        var stops = calculateGradient(colors);
        return stops;
    }

    function findGrad(dataurl, onload) {
        var image = new Image();
        image.src = dataurl;
        image.onload = function () {
            var img = this;
            var canvas = document.createElement("canvas");
            var ctx = canvas.getContext("2d");
            canvas.width = img.width;
            canvas.height = img.height;
            ctx.drawImage(img, 0, 0);
            onload(findGradFromCanvas(canvas));
        };
    }

    function colorsEqual(c1, c2) {
        var color1 = c1;
        var color2 = c2;
        if (c1.length) {
            color1 = new Color(c1);
        }
        if (c2.length) {
            color2 = new Color(c2);
        }

        return Color.prototype.equals.apply(color1, [color2]);
    }

    exports.GradientCalc = {
        findGrad: findGrad,
        findGradFromCanvas: findGradFromCanvas,
        colorsEqual: colorsEqual
    };
})(window);<commit_msg>fix an issue where occasionally I was off by 90%, occasionally had X/Y mixed up<commit_after>(function(exports) {
    function round(num, digits) {
        digits = digits || 0;
        return Math.round(num * Math.pow(10, digits)) / Math.pow(10, digits);
    }

    var Color = function(arr) {
        if (typeof(arr) === "string") {
            arr = arr.split(",");
        }

        this.r = arr[0];
        this.g = arr[1];
        this.b = arr[2];
        if (arr.length === 4) {
            this.a = round(arr[3] / 255, 2);
        }
        if (typeof(this.r) === 'undefined' || typeof(this.g) === 'undefined' || typeof(this.b) === 'undefined') {
            throw "Invalid Color Array passed in";
        }
    };

    Color.getAvg = function(a, b) {
        var color = [
            (a.r + b.r) / 2,
            (a.g + b.g) / 2,
            (a.b + b.b) / 2
        ];
        if ((typeof(a.a) !== 'undefined') || typeof(b.a !== 'undefined')) {
            a.a = (typeof a.a === 'undefined') ? 255 : a.a;
            b.a = (typeof b.a === 'undefined') ? 255 : b.a;
            color.push((a.a + b.a) / 2);
        }

        return new Color(color);
    };

    Color.prototype.equals = function(b, tolerance) {
        tolerance = (typeof(tolerance) === "undefined") ? 4 : tolerance;
        if (Math.abs(this.r - b.r) > tolerance) {
            return false;
        }
        if (Math.abs(this.g - b.g) > tolerance) {
            return false;
        }
        if (Math.abs(this.b - b.b) > tolerance) {
            return false;
        }
        // compare alphas -- treat undefined alpha as opaque
        var aAlpha = (typeof(this.a) === 'undefined') ? 1 : this.a;
        var bAlpha = (typeof(b.a) === 'undefined') ? 1 : b.a;
        var aTolerance = tolerance / 25;
        if (Math.abs(aAlpha - bAlpha) > tolerance) {
            return false;
        }

        return true;
    };

    Color.prototype.distanceFrom = function(color2) {
        // http://www.compuphase.com/cmetric.htm
        return Math.sqrt(
            3 * Math.pow(this.r - color2.r, 2) + 4 * Math.pow(this.g - color2.g, 2) + 2 * Math.pow(this.b - color2.b, 2) + Math.pow(this.a - color2.a, 2)
        );
    };

    Color.prototype.toString = function() {
        return (this.a === 1) ?
          "rgb("  + round(this.r) + ", " + round(this.g) + ", " + round(this.b) + ")" :
              "rgba(" + round(this.r) + ", " + round(this.g) + ", " + round(this.b) + ", " + round(this.a, 2) + ")";
    };

    function Gradient(stops, angle, length) {
        this.stops = stops;
        this.angle = angle;
    }

    Gradient.prototype.toCss = function() {
        if (this.stops.length === 1) {
            return "background-color: " + this.stops[0].color.toString();
       }

        var stops = this.stops.map(function(s) {
            return s.color.toString() + " " + round(s.idx * 100) + "%";
        });

        var css = this.angle + "deg, " + stops.join(",");
        return "background: -webkit-linear-gradient(" + css + ");\n" +
                "background: -o-linear-gradient(" + css + ");\n" +
                "background: -ms-linear-gradient(" + css + ");\n" +
                "background: -moz-linear-gradient(" + css + ");\n" +
                "background: linear-gradient(" + css + ");\n";
    };

    function getPixel(arr, x, y) {
        if (y === undefined) {
            return new Color(arr[x]);
        }

        return new Color(arr[y][x]);
    }

    // get the middle color of a 1 dimensional array
    function getMid(arr, start, end) {
        if (arr.length === 1) {
            return new Color(arr[0]);
        }

        start = start || 0;
        end = (end || arr.length) - start;
        arr = arr.slice(start);

        if (end % 2 === 0) {
            return getPixel(arr, end / 2);
        }
        else {
            return Color.getAvg(getPixel(arr, Math.floor(end / 2)), getPixel(arr, Math.ceil(end / 2)));
        }
    }

    function vectorToLine(angle, length) {
        function pointOfAngle(a) {
            function toRads(d) {
                return (d * Math.PI) / 180;
            }
            return {
                x: Math.max(Math.cos(toRads(a)), 0),
                y: Math.max(Math.sin(toRads(a)), 0)
            };
        }

        angle = angle % 360;
        var startPoint = pointOfAngle(180 - angle);
        var endPoint = pointOfAngle(0 - angle);

        return {
            x1: startPoint.x * length,
            y1: startPoint.y * length,
            x2: endPoint.x * length,
            y2: endPoint.y * length
        };
    }

    // returns unique values from a sorted array
    function uniqueArray(arr) {
        var ret = [];
        var last;

        for (var i = 0; i < arr.length; i++) {
            if (arr[i] !== last) {
                ret.push(arr[i]);
                last = arr[i];
            }
        }
        return ret;
    }

    function getAngle(arr) {
        function getSingleColorAngles(array) {
            var ret = [];
            for (var angle = 0; angle <= 180; angle++) {
                // check to see if the entire array is the same color
                var singlearr = getSingleDimensionalArray(array, angle).map(function(a) {
                    return a.join();// convert colors to strings (sorting multidimensional arrays is SLOW)
                });
                // simple case -- the arrays are 100% identical
                if (singlearr[0] === singlearr[singlearr.length - 1]) {
                    ret.push(angle);
                }
                else {
                    // slow case -- compare all unique color values with a threshold
                    var uniquevals = uniqueArray(singlearr);
                    var first = uniquevals[0];
                    for (var i = 1; i < uniquevals.length; i++) {
                        if (!colorsEqual(first, uniquevals[i])) {
                            break;
                        }
                        else {
                            if (i == uniquevals.length - 1) {
                                ret.push(angle);
                            }
                        }
                    }
                }
            }
            return ret;
        }

        function getLikely(array, possibles) {
            var sorted = possibles.map(function(angle) {
                var grad = getSingleDimensionalArray(array, angle);
                return {
                    angle: angle,
                    stops: getStops(grad)
                };
            });
            sorted = sorted.sort(function(a, b) {
                if (a.stops.length === b.stops.length) {
                    // for same # of stops, we want the angle that provides the most difference in colors
                    return a.angle - b.angle;
                }
                // single stops are an edge case, so de-prioritize them
                if (a.stops.length === 1) {
                    return 1;
                }
                if (b.stops.length === 1) {
                    return -1;
                }

                return a.stops.length - b.stops.length;
            });

            return sorted[0].angle;
        }

        var possibles = getSingleColorAngles(arr).map(function(angle) {
            return angle - 90;
        });

        var angle = getLikely(arr, possibles);
        if (typeof(angle) === "undefined") {
            throw "Couldn't find a gradient angle";
        }

        return angle;
    }

    function getSingleDimensionalArray(arr, angle) {
        var width = arr[0].length;
        var height = arr.length;
        var multiplier = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2));
        var vector = vectorToLine(angle, multiplier);
        var coords = bresenhamLine(round(vector.x1), round(vector.y1), round(vector.x2), round(vector.y2));

        var ret = [];
        for (var i = 0; i < coords.length; i++) {
            var x = coords[i][0];
            var y = coords[i][1];

            if (typeof(arr[y]) !== "undefined" && typeof(arr[y][x]) !== "undefined") {
                ret.push(arr[y][x]);
            }
        }

        return ret;
    }

    // http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
    function bresenhamLine(x1, y1, x2, y2) {
        if (isNaN(x1) || isNaN(y1) || isNaN(x2) || isNaN(y2)) {
            throw "Invalid coordinates for Bresenham's";
        }

        x1 = Math.round(x1);
        y1 = Math.round(y1);
        x2 = Math.round(x2);
        y2 = Math.round(y2);

        var coords = [];
        var dx = Math.abs(x2 - x1);
        var dy = Math.abs(y2 - y1);
        var sx = (x1 < x2) ? 1 : -1;
        var sy = (y1 < y2) ? 1 : -1;
        var err = dx - dy;

        coords.push([x1, y1]);
        while (!((x1 === x2) && (y1 === y2))) {
            var e2 = err * 2;
            if (e2 > -dy) {
                err -= dy;
                x1 += sx;
            }
            if (e2 < dx) {
                err += dx;
                y1 += sy;
            }
            // Set coordinates
            coords.push([x1, y1]);
        }

        return coords;
    }

    function getStops(arr, start, end) {
        var ret = [];
        start = start || 0;
        end = end || arr.length - 1;

        var startColor = getPixel(arr, start);
        var endColor = getPixel(arr, end);
        var average = Color.getAvg(startColor, endColor);
        var mid = getMid(arr, start, end);

        // we found a stop -- add it
        if (average.equals(mid)) {
            ret.push(start);

            // we're not at the end -- search between STOP and ARR.LEN for more stops
            if (end !== arr.length - 1) {
                ret = ret.concat(getStops(arr, end, arr.length - 1));
            }
            // found a stop at the end of the array -- break out of the loop
            else if (end === arr.length - 1 && !startColor.equals(endColor)) {
                ret.push(end);
            }
        }
        // no stop found -- try again
        else {
            ret = ret.concat(getStops(arr, start, end - 1));
        }

        return ret;
    }

    function calculateGradient(arr) {
        var angle = getAngle(arr);
        var lineArray = getSingleDimensionalArray(arr, angle);
        var stops = getStops(lineArray);
        var ret = stops.map(function(s) {
            var idx = round(s / (lineArray.length - 1), 2);
            return {
                idx: idx,
                color: getPixel(lineArray, s)
            };
        });

        return new Gradient(ret, angle);
    }

    function getColorArray(ctx) {
        var imageData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
        var width = imageData.width;
        var height = imageData.height;
        var data = imageData.data;

        var colors = [];
        for (var y = 0; y < height; y++) {
            var row = [];
            for (var x = 0; x < width; x++) {
                var idx = ((width * y) + x) * 4;
                var r = data[idx];
                var g = data[idx + 1];
                var b = data[idx + 2];
                var a = data[idx + 3];
                row.push([r, g, b, a]);
            }

            colors[y] = row;
        }

        return colors;
    }

    function findGradFromCanvas(canvas) {
        var ctx = canvas.getContext("2d");
        var colors = getColorArray(ctx);
        var stops = calculateGradient(colors);
        return stops;
    }

    function findGrad(dataurl, onload) {
        var image = new Image();
        image.src = dataurl;
        image.onload = function () {
            var img = this;
            var canvas = document.createElement("canvas");
            var ctx = canvas.getContext("2d");
            canvas.width = img.width;
            canvas.height = img.height;
            ctx.drawImage(img, 0, 0);
            onload(findGradFromCanvas(canvas));
        };
    }

    function colorsEqual(c1, c2) {
        var color1 = c1;
        var color2 = c2;
        if (c1.length) {
            color1 = new Color(c1);
        }
        if (c2.length) {
            color2 = new Color(c2);
        }

        return Color.prototype.equals.apply(color1, [color2]);
    }

    exports.GradientCalc = {
        findGrad: findGrad,
        findGradFromCanvas: findGradFromCanvas,
        colorsEqual: colorsEqual
    };
})(window);<|endoftext|>


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

foam.CLASS({
  package: 'foam.dao',
  name: 'DAOProperty',
  extends: 'FObjectProperty',

  documentation: 'Property for storing a reference to a DAO.',

  requires: [ 'foam.dao.ProxyDAO' ],

  properties: [
    {
      name: 'view',
      value: {
        class: 'foam.u2.view.ModeAltView',
        readView: {
          class: 'foam.u2.view.ScrollTableView',
          enableDynamicTableHeight: false
        },
        writeView: { class: 'foam.comics.InlineBrowserView' }
      }
    },
    {
      name: 'createVisibility',
      value: 'HIDDEN'
    },
    ['transient', true],
    ['of', 'foam.dao.DAO'],
    {
      name: 'javaInfoType',
      flags: ['java'],
      value: 'foam.core.AbstractDAOPropertyPropertyInfo',
    },
  ],

  methods: [
    function installInProto(proto) {
      this.SUPER(proto);

      var name = this.name;
      var prop = this;

      Object.defineProperty(proto, name + '$proxy', {
        get: function daoProxyGetter() {
          var proxy = prop.ProxyDAO.create({delegate: this[name]}, this[name]);
          this[name + '$proxy'] = proxy;

          this.sub('propertyChange', name, function(_, __, ___, s) {
            proxy.delegate = s.get();
          });

          return proxy;
        },
        configurable: true
      });
    }
  ]
});
<commit_msg>Formatting.<commit_after>/**
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.CLASS({
  package: 'foam.dao',
  name: 'DAOProperty',
  extends: 'FObjectProperty',

  documentation: 'Property for storing a reference to a DAO.',

  requires: [ 'foam.dao.ProxyDAO' ],

  properties: [
    {
      name: 'view',
      value: {
        class: 'foam.u2.view.ModeAltView',
        readView: {
          class: 'foam.u2.view.ScrollTableView',
          enableDynamicTableHeight: false
        },
        writeView: { class: 'foam.comics.InlineBrowserView' }
      }
    },
    {
      name: 'createVisibility',
      value: 'HIDDEN'
    },
    ['transient', true],
    ['of', 'foam.dao.DAO'],
    {
      name: 'javaInfoType',
      flags: ['java'],
      value: 'foam.core.AbstractDAOPropertyPropertyInfo'
    }
  ],

  methods: [
    function installInProto(proto) {
      this.SUPER(proto);

      var name = this.name;
      var prop = this;

      Object.defineProperty(proto, name + '$proxy', {
        get: function daoProxyGetter() {
          var proxy = prop.ProxyDAO.create({delegate: this[name]}, this[name]);
          this[name + '$proxy'] = proxy;

          this.sub('propertyChange', name, function(_, __, ___, s) {
            proxy.delegate = s.get();
          });

          return proxy;
        },
        configurable: true
      });
    }
  ]
});
<|endoftext|>