<commit_before>var animationTime = 200; // ms

$(function() {
    // Register event to show event group forms
    $('#id_event-group-enabled').on('change', function(ev) {
        if (ev.target.checked) {
            $('#id_event-group').prop('disabled', false)
            $('.event-group').slideDown(animationTime);
        } else {
            $('.event-group').slideUp(animationTime);
            $('#id_event-group').prop('disabled', true)
        }
    });

    // Initialise datetimepicker's
    $('.js-datetimepicker').datetimepicker({
        format: 'dd/mm/yyyy hh:ii',
        autoclose: true,
    });
    $('#event-start.js-datetimepicker').on('changeDate', function(ev) {
        //set the end time to 1 hour later
        hours = ev.date.getHours();
        var date = new Date(ev.date);
        date.setHours(date.getHours()+1, date.getMinutes());
        $('#event-end.js-datetimepicker').data('datetimepicker').setDate(date);
    });

    $('#create-group-button').data('successCallback', function(newGroup) {
        $('<option>').attr('value', newGroup.id).text(newGroup.title).appendTo('#id_event-group').prop('selected', true);

        //update the event's department, if the newly created group has it set
        if (newGroup.department_organiser != null) {
            updateEventDepartment(newGroup.department_organiser);
        }
    })

    $('.js-create-person-control').each( function() {
        //reveal the form panel on clicking the reveal link
        $(this).find('.js-create-person').on('click', function(ev) {
            ev.preventDefault();
            var $control = $(ev.target).parent().parent();
            $control.find('.js-person-panel').slideDown(animationTime);
        })

        //capture the click on 'Add' and update the relevant control
        $(this).find('.js-submit-person').on('click', function(ev) {
            var $control = $(ev.target).parent().parent();

            var namefield = $control.find('#id_name');
            var biofield = $control.find('#id_bio');
            var csrftoken = $.cookie('csrftoken');
            var $errorMessage = $control.find('.js-person-form-errors');
            var target = $(this).attr('data-input-target');
            var $target = $(target);
            $.ajax({
                    type: 'POST',
                    url: '/api/persons/new',
                    headers: {
                        "X-CSRFToken": csrftoken,
                    },
                    data: {
                        name: namefield.val(),
                        bio: biofield.val()
                    },

                    success: function(response) {
                        $target.trigger("addPerson", response);
                        namefield.val("");
                        biofield.val("");
                        //clear error classes
                        setErrorStateForInput($control, 'name', false);
                        setErrorStateForInput($control, 'bio', false);
                        //clear and hide error message
                        $errorMessage.addClass("hidden");
                    },
                    error: function(response) {
                        setErrorStateForInput($control, 'name', false);
                        setErrorStateForInput($control, 'bio', false)
                        for(key in response.responseJSON) {
                            setErrorStateForInput($control, key, true)
                        }
                        $errorMessage.removeClass("hidden");
                        $errorMessage.html("Missing required field");
                    }
                }
            );
        })
    });

    function setErrorStateForInput(control, id, on) {
        var el = control.find("#id_"+id).parent().parent();
        if(on) {
            el.addClass("has-error");
        }
        else {
            el.removeClass("has-error");
        }
    }

    function updateEventDepartment(location_id) {
        if(!location_id) { return; }
        $.ajax({
                    type:'GET',
                    url: '//api.m.ox.ac.uk/places/' + location_id,
                    success: function(response) {
                        $('#id_event-department_organiser').trigger("eventGroupChanged", response);
                    },
                    error: function(err) {
                        console.log(err);
                    }
                })
    }

    //On picking a new event group, retrieve the information and set the value of the department organiser field
    $('#id_event-group').change( function() {
        var groupID = this.value;
        if(!groupID) {
            //User has probably selected the 'Please select' option
            return;
        }
        var url = '/api/series/id/' + groupID

        //retrieve the ID of the event organiser and apply that to the department field of the form
        $.ajax({
            type: 'GET',
            url: url,
            success: function(response) {
                //retrieve the name of the location in question
                updateEventDepartment(response.department_organiser);
            },
            error: function(err) {
                console.log(err);
            }
        });

    })
});
<commit_msg>Fix for end time being set to two hours later for events starting during BST #218<commit_after>var animationTime = 200; // ms

$(function() {
    // Register event to show event group forms
    $('#id_event-group-enabled').on('change', function(ev) {
        if (ev.target.checked) {
            $('#id_event-group').prop('disabled', false)
            $('.event-group').slideDown(animationTime);
        } else {
            $('.event-group').slideUp(animationTime);
            $('#id_event-group').prop('disabled', true)
        }
    });

    // Initialise datetimepicker's
    $('.js-datetimepicker').datetimepicker({
        format: 'dd/mm/yyyy hh:ii',
        autoclose: true,
    });

    $('#event-start.js-datetimepicker').on('changeDate', function(ev) {
        //correct for daylight savings - the widget assumes we're picking in GMT
        var date = new Date(ev.date);
        var gmt_offset = date.getTimezoneOffset();
        date.setMinutes( date.getMinutes() + gmt_offset );
        var picker = $(ev.target).data('datetimepicker');
        picker.setDate(date);

        //set the end time to 1 hour later
        var end_date = new Date(date);
        date.setHours(date.getHours()+1, date.getMinutes());
        $('#event-end.js-datetimepicker').data('datetimepicker').setDate(date);
    });

    $('#create-group-button').data('successCallback', function(newGroup) {
        $('<option>').attr('value', newGroup.id).text(newGroup.title).appendTo('#id_event-group').prop('selected', true);

        //update the event's department, if the newly created group has it set
        if (newGroup.department_organiser != null) {
            updateEventDepartment(newGroup.department_organiser);
        }
    })

    $('.js-create-person-control').each( function() {
        //reveal the form panel on clicking the reveal link
        $(this).find('.js-create-person').on('click', function(ev) {
            ev.preventDefault();
            var $control = $(ev.target).parent().parent();
            $control.find('.js-person-panel').slideDown(animationTime);
        })

        //capture the click on 'Add' and update the relevant control
        $(this).find('.js-submit-person').on('click', function(ev) {
            var $control = $(ev.target).parent().parent();

            var namefield = $control.find('#id_name');
            var biofield = $control.find('#id_bio');
            var csrftoken = $.cookie('csrftoken');
            var $errorMessage = $control.find('.js-person-form-errors');
            var target = $(this).attr('data-input-target');
            var $target = $(target);
            $.ajax({
                    type: 'POST',
                    url: '/api/persons/new',
                    headers: {
                        "X-CSRFToken": csrftoken,
                    },
                    data: {
                        name: namefield.val(),
                        bio: biofield.val()
                    },

                    success: function(response) {
                        $target.trigger("addPerson", response);
                        namefield.val("");
                        biofield.val("");
                        //clear error classes
                        setErrorStateForInput($control, 'name', false);
                        setErrorStateForInput($control, 'bio', false);
                        //clear and hide error message
                        $errorMessage.addClass("hidden");
                    },
                    error: function(response) {
                        setErrorStateForInput($control, 'name', false);
                        setErrorStateForInput($control, 'bio', false)
                        for(key in response.responseJSON) {
                            setErrorStateForInput($control, key, true)
                        }
                        $errorMessage.removeClass("hidden");
                        $errorMessage.html("Missing required field");
                    }
                }
            );
        })
    });

    function setErrorStateForInput(control, id, on) {
        var el = control.find("#id_"+id).parent().parent();
        if(on) {
            el.addClass("has-error");
        }
        else {
            el.removeClass("has-error");
        }
    }

    function updateEventDepartment(location_id) {
        if(!location_id) { return; }
        $.ajax({
                    type:'GET',
                    url: '//api.m.ox.ac.uk/places/' + location_id,
                    success: function(response) {
                        $('#id_event-department_organiser').trigger("eventGroupChanged", response);
                    },
                    error: function(err) {
                        console.log(err);
                    }
                })
    }

    //On picking a new event group, retrieve the information and set the value of the department organiser field
    $('#id_event-group').change( function() {
        var groupID = this.value;
        if(!groupID) {
            //User has probably selected the 'Please select' option
            return;
        }
        var url = '/api/series/id/' + groupID

        //retrieve the ID of the event organiser and apply that to the department field of the form
        $.ajax({
            type: 'GET',
            url: url,
            success: function(response) {
                //retrieve the name of the location in question
                updateEventDepartment(response.department_organiser);
            },
            error: function(err) {
                console.log(err);
            }
        });

    })
});
<|endoftext|>


<commit_before>define("Ti/Network/HTTPClient", ["Ti/_", "Ti/_/declare", "Ti/_/lang", "Ti/_/Evented", "Ti/Network"], function(_, declare, lang, Evented, Network) {

	var is = require.is,
		on = require.on,
		enc = encodeURIComponent,
		undef;

	return declare("Ti.Network.HTTPClient", Evented, {

		constructor: function() {
			var xhr = this._xhr = new XMLHttpRequest;

			on(xhr, "error", this, "_onError");
			on(xhr.upload, "error", this, "_onError");

			on(xhr, "progress", this, "_onProgress");
			on(xhr.upload, "progress", this, "_onProgress");

			xhr.onreadystatechange = lang.hitch(this, function() {
				var c = this.constants;
				switch (xhr.readyState) {
					case 0: c.readyState = this.UNSENT; break;
					case 1: c.readyState = this.OPENED; break;
					case 2: c.readyState = this.LOADING; break;
					case 3: c.readyState = this.HEADERS_RECEIVED; break;
					case 4:
						clearTimeout(this._timeoutTimer);
						this._completed = 1;
						c.readyState = this.DONE;
						if (xhr.status == 200) {
							c.responseText = xhr.responseText;
							c.responseXML = xhr.responseXML;
							c.responseData = xhr.responseHeader;
							is(this.onload, "Function") && this.onload.call(this);
						} else {
							xhr.status / 100 | 0 > 3 && this._onError();
						}
				}
				this._fireStateChange();
			});
		},

		destroy: function() {
			if (this._xhr) {
				this._xhr.abort();
				this._xhr = null;
			}
		},

		_onError: function(error) {
			this.abort();
			is(error, "Object") || (error = { message: error });
			error.error || (error.error = error.message || xhr.status);
			parseInt(error.error) || (error.error = "Can't reach host");
			is(this.onerror, "Function") && this.onerror.call(this, error);
		},

		_onProgress: function(evt) {
			evt.progress = evt.lengthComputable ? evt.loaded / evt.total : false;
			is(this.onsendstream, "Function") && this.onsendstream.call(this, evt);
		},

		abort: function() {
			var c = this.constants;
			c.responseText = c.responseXML = c.responseData = "";
			this._completed = true;
			clearTimeout(this._timeoutTimer);
			this.connected && this._xhr.abort();
			c.readyState = this.UNSENT;
			this._fireStateChange();
		},

		_fireStateChange: function() {
			is(this.onreadystatechange, "Function") && this.onreadystatechange.call(this);
		},

		getResponseHeader: function(name) {
			return this._xhr.readyState > 1 ? this._xhr.getResponseHeader(name) : null;
		},

		open: function(method, url, async) {
			var httpURLFormatter = Ti.Network.httpURLFormatter,
				c = this.constants;
			this.abort();
			this._xhr.open(
				c.connectionType = method,
				c.location = _.getAbsolutePath(httpURLFormatter ? httpURLFormatter(url) : url),
				!!async
			);
			this._xhr.setRequestHeader("UserAgent", Ti.userAgent);
		},

		send: function(args){
			try {
				var timeout = this.timeout | 0;
				this._completed = false;
				args = is(args, "Object") ? lang.urlEncode(args) : args;
				args && this._xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
				this._xhr.send(args);
				clearTimeout(this._timeoutTimer);
				timeout && (this._timeoutTimer = setTimeout(lang.hitch(this, function() {
					if (this.connected) {
						this.abort();
						!this._completed && this._onError("Request timed out");
					}
				}, timeout)));
			} catch (ex) {}
		},

		setRequestHeader: function(name, value) {
			this._xhr.setRequestHeader(name, value);
		},

		properties: {
			ondatastream: undef,
			onerror: undef,
			onload: undef,
			onreadystatechange: undef,
			onsendstream: undef,
			timeout: undef
		},

		constants: {
			DONE: 4,

			HEADERS_RECEIVED: 2,

			LOADING: 3,

			OPENED: 1,

			UNSENT: 1,

			connected: function() {
				return this.readyState >= this.OPENED;
			},

			connectionType: undef,

			location: undef,

			readyState: this.UNSENT,

			responseData: undef,

			responseText: undef,

			responseXML: undef,

			status: function() {
				return this._xhr.status;
			},

			statusText: function() {
				return this._xhr.statusText;
			}
		}

	});

});
<commit_msg>[TIMOB-7546] Fixed responseData to store the responseText.<commit_after>define("Ti/Network/HTTPClient", ["Ti/_", "Ti/_/declare", "Ti/_/lang", "Ti/_/Evented", "Ti/Network"], function(_, declare, lang, Evented, Network) {

	var is = require.is,
		on = require.on,
		enc = encodeURIComponent,
		undef;

	return declare("Ti.Network.HTTPClient", Evented, {

		constructor: function() {
			var xhr = this._xhr = new XMLHttpRequest;

			on(xhr, "error", this, "_onError");
			on(xhr.upload, "error", this, "_onError");

			on(xhr, "progress", this, "_onProgress");
			on(xhr.upload, "progress", this, "_onProgress");

			xhr.onreadystatechange = lang.hitch(this, function() {
				var c = this.constants;
				switch (xhr.readyState) {
					case 0: c.readyState = this.UNSENT; break;
					case 1: c.readyState = this.OPENED; break;
					case 2: c.readyState = this.LOADING; break;
					case 3: c.readyState = this.HEADERS_RECEIVED; break;
					case 4:
						clearTimeout(this._timeoutTimer);
						this._completed = 1;
						c.readyState = this.DONE;
						if (xhr.status == 200) {
							c.responseText = c.responseData = xhr.responseText;
							c.responseXML = xhr.responseXML;
							is(this.onload, "Function") && this.onload.call(this);
						} else {
							xhr.status / 100 | 0 > 3 && this._onError();
						}
				}
				this._fireStateChange();
			});
		},

		destroy: function() {
			if (this._xhr) {
				this._xhr.abort();
				this._xhr = null;
			}
		},

		_onError: function(error) {
			this.abort();
			is(error, "Object") || (error = { message: error });
			error.error || (error.error = error.message || xhr.status);
			parseInt(error.error) || (error.error = "Can't reach host");
			is(this.onerror, "Function") && this.onerror.call(this, error);
		},

		_onProgress: function(evt) {
			evt.progress = evt.lengthComputable ? evt.loaded / evt.total : false;
			is(this.onsendstream, "Function") && this.onsendstream.call(this, evt);
		},

		abort: function() {
			var c = this.constants;
			c.responseText = c.responseXML = c.responseData = "";
			this._completed = true;
			clearTimeout(this._timeoutTimer);
			this.connected && this._xhr.abort();
			c.readyState = this.UNSENT;
			this._fireStateChange();
		},

		_fireStateChange: function() {
			is(this.onreadystatechange, "Function") && this.onreadystatechange.call(this);
		},

		getResponseHeader: function(name) {
			return this._xhr.readyState > 1 ? this._xhr.getResponseHeader(name) : null;
		},

		open: function(method, url, async) {
			var httpURLFormatter = Ti.Network.httpURLFormatter,
				c = this.constants;
			this.abort();
			this._xhr.open(
				c.connectionType = method,
				c.location = _.getAbsolutePath(httpURLFormatter ? httpURLFormatter(url) : url),
				!!async
			);
			this._xhr.setRequestHeader("UserAgent", Ti.userAgent);
		},

		send: function(args){
			try {
				var timeout = this.timeout | 0;
				this._completed = false;
				args = is(args, "Object") ? lang.urlEncode(args) : args;
				args && this._xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
				this._xhr.send(args);
				clearTimeout(this._timeoutTimer);
				timeout && (this._timeoutTimer = setTimeout(lang.hitch(this, function() {
					if (this.connected) {
						this.abort();
						!this._completed && this._onError("Request timed out");
					}
				}, timeout)));
			} catch (ex) {}
		},

		setRequestHeader: function(name, value) {
			this._xhr.setRequestHeader(name, value);
		},

		properties: {
			ondatastream: undef,
			onerror: undef,
			onload: undef,
			onreadystatechange: undef,
			onsendstream: undef,
			timeout: undef
		},

		constants: {
			DONE: 4,

			HEADERS_RECEIVED: 2,

			LOADING: 3,

			OPENED: 1,

			UNSENT: 1,

			connected: function() {
				return this.readyState >= this.OPENED;
			},

			connectionType: undef,

			location: undef,

			readyState: this.UNSENT,

			responseData: undef,

			responseText: undef,

			responseXML: undef,

			status: function() {
				return this._xhr.status;
			},

			statusText: function() {
				return this._xhr.statusText;
			}
		}

	});

});
<|endoftext|>


<commit_before>if (typeof Meteor === "undefined") Meteor = {};

// list of subscription tokens outstanding during a
// captureDependencies run. only set when we're doing a run. The fact
// that this is a singleton means we can't do recursive
// Meteor.subscriptions(). But who wants that? What does that even mean?
// XXX namespacing
Meteor._capture_subs = null;

Meteor.Server = function (url) {
  var self = this;
  self.url = url;
  self.collections = {}; // name -> Collection-type object
  self.method_handlers = {}; // name -> func
  self.next_method_id = 1;
  self.pending_method_callbacks = {}; // map from method_id -> result function
  self.pending_method_messages = {}; // map from method_id -> message to server
  self.unsatisfied_methods = {}; // map from method_id -> true
  self.pending_data = []; // array of pending data messages

  self.subs = new Collection;
  // keyed by subs._id. value is unset or an array. if set, sub is not
  // yet ready.
  self.sub_ready_callbacks = {};

  // Setup auto-reload persistence.
  var reload_key = "Server-" + url;
  var reload_data = Meteor._reload.migration_data(reload_key);
  if (typeof reload_data === "object") {
    if (typeof reload_data.next_method_id === "number")
      self.next_method_id = reload_data.next_method_id;
    if (typeof reload_data.pending_method_messages === "object")
      self.pending_method_messages = reload_data.pending_method_messages;
    // pending messages will be transmitted on initial stream 'reset'
  }
  Meteor._reload.on_migrate(reload_key, function () {
    return { next_method_id: self.next_method_id,
             pending_method_messages: self.pending_method_messages };
  });

  // Setup stream
  self.stream = new Meteor._Stream(self.url);

  self.stream.on('message', function (raw_msg) {
    try {
      var msg = JSON.parse(raw_msg);
    } catch (err) {
      Meteor._debug("discarding message with invalid JSON", raw_msg);
      return;
    }
    if (typeof msg !== 'object' || !msg.msg) {
      Meteor._debug("discarding invalid livedata message", msg);
      return;
    }

    if (msg.msg === 'connected')
      self._livedata_connected(msg);
    else if (msg.msg === 'data')
      self._livedata_data(msg);
    else if (msg.msg === 'nosub')
      self._livedata_nosub(msg);
    else if (msg.msg === 'result')
      self._livedata_result(msg);
    else
      Meteor._debug("discarding unknown livedata message type", msg);
  });

  self.stream.on('reset', function () {

    // Send a connect message at the beginning of the stream.
    // NOTE: reset is called even on the first connection, so this is
    // the only place we send this message.
    self.stream.send(JSON.stringify({msg: 'connect'}));

    // Send pending methods.
    _.each(self.pending_method_messages, function (msg) {
      self.stream.send(JSON.stringify(msg));
    });

    // add new subscriptions at the end. this way they take effect after
    // the handlers and we don't see flicker.
    self.subs.find().forEach(function (sub) {
      self.stream.send(JSON.stringify(
        {msg: 'sub', id: sub._id, name: sub.name, params: sub.args}));
    });

    // clear out the local database!
    _.each(self.collections, function (col) {
      col._collection.remove({});
    });

  });

  // we never terminate the observe(), since there is no way to
  // destroy a Server.. but this shouldn't matter, since we're the
  // only one that holds a reference to the self.subs collection
  self.subs_token = self.subs.find({}).observe({
    added: function (sub) {
      self.stream.send(JSON.stringify({
        msg: 'sub', id: sub._id, name: sub.name, params: sub.args}));
    },
    changed: function (sub) {
      if (sub.count <= 0) {
        // minimongo not re-entrant.
        _.defer(function () { self.subs.remove({_id: sub._id}); });
      }
    },
    removed: function (id) {
      self.stream.send(JSON.stringify({msg: 'unsub', id: id}));
    }
  });
};

_.extend(Meteor.Server.prototype, {
  subscribe: function (name, args, callback) {
    var self = this;
    var id;
    var existing = self.subs.find({name: name, args: args}, {reactive: false}).fetch();

    if (existing && existing[0]) {
      // already subbed, inc count.
      id = existing[0]._id;
      self.subs.update({_id: id}, {$inc: {count: 1}});

      if (callback) {
        if (self.sub_ready_callbacks[id])
          self.sub_ready_callbacks[id].push(callback);
        else
          callback(); // XXX maybe _.defer?
      }
    } else {
      // new sub, add object.
      // generate our own id so we can know it w/ a find afterwards.
      id = Collection.uuid();
      self.subs.insert({_id: id, name: name, args: args, count: 1});

      self.sub_ready_callbacks[id] = [];

      if (callback)
        self.sub_ready_callbacks[id].push(callback);
    }

    // return an object with a stop method.
    var token = {stop: function () {
      if (!id) return; // must have an id (local from above).
      // just update the database. observe takes care of the rest.
      self.subs.update({_id: id}, {$inc: {count: -1}});
    }};

    if (Meteor._capture_subs)
      Meteor._capture_subs.push(token);

    return token;
  },

  methods: function (methods) {
    var self = this;
    _.each(methods, function (func, name) {
      if (self.method_handlers[name])
        throw new Error("A method named '" + name + "' is already defined");
      self.method_handlers[name] = func;
    });
  },

  call: function (name /*, arguments */) {
    return this.apply(name, Array.prototype.slice.call(arguments, 1));
  },

  apply: function (name, args) {
    var self = this;
    var handler = self.method_handlers[name];

    args = _.clone(args);
    var result_func = function () {};
    if (args.length && typeof args[args.length - 1] === "function")
      result_func = args.pop();

    if (handler) {
      // run locally (if available)
      var local_args = _.clone(args);
      local_args.unshift(self.user_id);
      var ret = handler.apply(null, args);
    }

    // run on server
    self._send_method(
      {msg: 'method', method: name, params: args},
      result_func);
  },

  _send_method: function (msg, result_func) {
    var self = this;
    var method_id = self.next_method_id++;
    var new_msg = _.extend({id: method_id}, msg);
    self.pending_method_messages[method_id] = new_msg;
    self.pending_method_callbacks[method_id] = result_func || function () {};
    self.unsatisfied_methods[method_id] = true;

    self.stream.send(JSON.stringify(new_msg));
  },

  _livedata_connected: function (msg) {
    var self = this;
    // Meteor._debug("CONNECTED", msg);
  },

  _livedata_data: function (msg) {
    var self = this;

    // Add the data message to the queue
    self.pending_data.push(msg);

    // If there are still method invocations in flight, stop
    _.each(msg.methods || [], function (method_id) {
      delete self.unsatisfied_methods[method_id];
    });
    for (var method_id in self.unsatisfied_methods)
      return;

    // All methods have landed. Blow away local changes and replace
    // with authoritative changes from server.

    _.each(self.collections, function (coll) {
      if (coll._was_snapshot) {
        coll._collection.restore(); // Revert all local changes
        coll._was_snapshot = false;
      }
    });

    _.each(self.pending_data, function (msg) {
      if (msg.collection && msg.id) {
        var meteor_coll = self.collections[msg.collection];

        if (!meteor_coll) {
          Meteor._debug(
            "discarding data received for unknown collection " +
              JSON.stringify(msg.collection));
          return;
        }

        // do all the work against underlying minimongo collection.
        var coll = meteor_coll._collection;

        var doc = coll.findOne(msg.id);

        if (doc
            && (!msg.set || msg.set.length === 0)
            && _.difference(_.keys(doc), msg.unset, ['_id']).length === 0) {
          // what's left is empty, just remove it.  cannot fail.
          coll.remove(msg.id);
        } else if (doc) {
          var mutator = {$set: msg.set, $unset: {}};
          _.each(msg.unset, function (propname) {
            mutator.$unset[propname] = 1;
          });
          // XXX error check return value from update.
          coll.update(msg.id, mutator);
        } else {
          // XXX error check return value from insert.
          coll.insert(_.extend({_id: msg.id}, msg.set));
        }
      }

      if (msg.subs) {
        _.each(msg.subs, function (id) {
          var arr = self.sub_ready_callbacks[id];
          if (arr) _.each(arr, function (c) { c(); });
          delete self.sub_ready_callbacks[id];
        });
      }
    });

    self.pending_data = [];
  },

  _livedata_nosub: function (msg) {
    var self = this;
    // Meteor._debug("NOSUB", msg);
  },

  _livedata_result: function (msg) {
    var self = this;
    // id, result or error. error has error (code), reason, details

    if (('id' in msg) && (msg.id in self.pending_method_messages)) {
      delete self.pending_method_messages[msg.id];
    } else {
      // XXX write a better error
      Meteor._debug("Can't interpret method response message");
    }

    if (('id' in msg) && (msg.id in self.pending_method_callbacks)) {
      var func = self.pending_method_callbacks[msg.id];
      delete self.pending_method_callbacks[msg.id];
      // XXX wrap in try..catch?
      if ('error' in msg)
        func(msg.error);
      else
        func(undefined, msg.result);
    } else {
      // If a response is to a method we sent from reload pending methods,
      // it won't have a callback. This is OK. This will get revisited
      // when we do more intellegent auto-reloads where we wait to
      // quiesce, pre-stage cached assets, etc.
    }
  }
});

Meteor._Collection = function (name, server) {
  var self = this;

  if (name && (name in server.collections))
    // maybe should just return server.collections[name]?
    throw new Error("There is already a remote collection '" + name + "'");

  self._name = name;
  self._collection = new Collection;
  self._server = server;
  self._was_snapshot = false;

  if (name)
    server.collections[name] = self;
};

_.extend(Meteor._Collection.prototype, {
  find: function (/* selector, options */) {
    var self = this;
    // Collection.find() (return all docs) behaves differently
    // from Collection.find(undefined) (return 0 docs).  so be
    // careful about preserving the length of arguments when
    // descending into minimongo.
    return self._collection.find.apply(self._collection, Array.prototype.slice.call(arguments));
  },

  findOne: function (/* selector, options */) {
    var self = this;
    // as above
    return self._collection.findOne.apply(self._collection, Array.prototype.slice.call(arguments));
  },

  _maybe_snapshot: function () {
    var self = this;
    if (!self._was_snapshot) {
      self._collection.snapshot();
      self._was_snapshot = true;
    }
  },

  // XXX provide a way for the caller to find out about errors from the server?
  insert: function (obj) {
    var self = this;
    // Generate an id for the object.
    // XXX mutates the object passed in. that is not cool.
    if (obj._id)
      Meteor._debug("WARNING: trying to insert object w/ _id set");
    var _id = Collection.uuid();
    obj._id = _id;

    if (self._name) {
      self._maybe_snapshot();
      self._server._send_method({
        msg: 'method',
        method: '/' + self._name + '/insert',
        params: [obj]});
    }
    self._collection.insert(obj);

    return obj;
  },

  // XXX provide a way for the caller to find out about errors from the server?
  update: function (selector, mutator, options) {
    var self = this;
    if (self._name) {
      self._maybe_snapshot();
      self._server._send_method({
        msg: 'method',
        method: '/' + self._name + '/update',
        params: [selector, mutator, options]});
    }
    self._collection.update(selector, mutator, options);
  },

  // XXX provide a way for the caller to find out about errors from the server?
  remove: function (selector) {
    var self = this;

    if (arguments.length === 0)
      selector = {};

    if (self._name) {
      self._maybe_snapshot();
      self._server._send_method({
        msg: 'method',
        method: '/' + self._name + '/remove',
        params: [selector]});
    }
    self._collection.remove(selector);
  },

  schema: function  () {
    // XXX not implemented yet
  }
});

// Path matches sockjs 'prefix' in stream_server. We should revisit this
// once we specify the 'on the wire' aspects of livedata more clearly.
App = new Meteor.Server('/sockjs');

_.extend(Meteor, {
  is_server: false,
  is_client: true,

  // XXX these are wrong
  status: function () {
    return App.stream.status();
  },

  reconnect: function () {
    return App.stream.reconnect();
  },

  publish: function() {
    // ignored on the client
  },

  // XXX make the user create it directly, with 'new'
  Collection: function (name) {
    return new Meteor._Collection(name, App);
  },

  subscribe: function (/* arguments */) {
    return App.subscribe.apply(App, _.toArray(arguments));
  },

  autosubscribe: function (sub_func) {
    var local_subs = [];
    var context = new Meteor.deps.Context();

    context.on_invalidate(function () {
      // recurse.
      Meteor.autosubscribe(sub_func);
      // unsub after re-subbing, to avoid bouncing.
      _.each(local_subs, function (x) { x.stop() });
    });

    context.run(function () {
      if (Meteor._capture_subs)
        throw new Error("Meteor.autosubscribe may not be called recursively");

      Meteor._capture_subs = [];
      try {
        sub_func();
      } finally {
        local_subs = Meteor._capture_subs;
        Meteor._capture_subs = null;
      }
    });
  }
});
<commit_msg>Warn on empty object insert.<commit_after>if (typeof Meteor === "undefined") Meteor = {};

// list of subscription tokens outstanding during a
// captureDependencies run. only set when we're doing a run. The fact
// that this is a singleton means we can't do recursive
// Meteor.subscriptions(). But who wants that? What does that even mean?
// XXX namespacing
Meteor._capture_subs = null;

Meteor.Server = function (url) {
  var self = this;
  self.url = url;
  self.collections = {}; // name -> Collection-type object
  self.method_handlers = {}; // name -> func
  self.next_method_id = 1;
  self.pending_method_callbacks = {}; // map from method_id -> result function
  self.pending_method_messages = {}; // map from method_id -> message to server
  self.unsatisfied_methods = {}; // map from method_id -> true
  self.pending_data = []; // array of pending data messages

  self.subs = new Collection;
  // keyed by subs._id. value is unset or an array. if set, sub is not
  // yet ready.
  self.sub_ready_callbacks = {};

  // Setup auto-reload persistence.
  var reload_key = "Server-" + url;
  var reload_data = Meteor._reload.migration_data(reload_key);
  if (typeof reload_data === "object") {
    if (typeof reload_data.next_method_id === "number")
      self.next_method_id = reload_data.next_method_id;
    if (typeof reload_data.pending_method_messages === "object")
      self.pending_method_messages = reload_data.pending_method_messages;
    // pending messages will be transmitted on initial stream 'reset'
  }
  Meteor._reload.on_migrate(reload_key, function () {
    return { next_method_id: self.next_method_id,
             pending_method_messages: self.pending_method_messages };
  });

  // Setup stream
  self.stream = new Meteor._Stream(self.url);

  self.stream.on('message', function (raw_msg) {
    try {
      var msg = JSON.parse(raw_msg);
    } catch (err) {
      Meteor._debug("discarding message with invalid JSON", raw_msg);
      return;
    }
    if (typeof msg !== 'object' || !msg.msg) {
      Meteor._debug("discarding invalid livedata message", msg);
      return;
    }

    if (msg.msg === 'connected')
      self._livedata_connected(msg);
    else if (msg.msg === 'data')
      self._livedata_data(msg);
    else if (msg.msg === 'nosub')
      self._livedata_nosub(msg);
    else if (msg.msg === 'result')
      self._livedata_result(msg);
    else
      Meteor._debug("discarding unknown livedata message type", msg);
  });

  self.stream.on('reset', function () {

    // Send a connect message at the beginning of the stream.
    // NOTE: reset is called even on the first connection, so this is
    // the only place we send this message.
    self.stream.send(JSON.stringify({msg: 'connect'}));

    // Send pending methods.
    _.each(self.pending_method_messages, function (msg) {
      self.stream.send(JSON.stringify(msg));
    });

    // add new subscriptions at the end. this way they take effect after
    // the handlers and we don't see flicker.
    self.subs.find().forEach(function (sub) {
      self.stream.send(JSON.stringify(
        {msg: 'sub', id: sub._id, name: sub.name, params: sub.args}));
    });

    // clear out the local database!
    _.each(self.collections, function (col) {
      col._collection.remove({});
    });

  });

  // we never terminate the observe(), since there is no way to
  // destroy a Server.. but this shouldn't matter, since we're the
  // only one that holds a reference to the self.subs collection
  self.subs_token = self.subs.find({}).observe({
    added: function (sub) {
      self.stream.send(JSON.stringify({
        msg: 'sub', id: sub._id, name: sub.name, params: sub.args}));
    },
    changed: function (sub) {
      if (sub.count <= 0) {
        // minimongo not re-entrant.
        _.defer(function () { self.subs.remove({_id: sub._id}); });
      }
    },
    removed: function (id) {
      self.stream.send(JSON.stringify({msg: 'unsub', id: id}));
    }
  });
};

_.extend(Meteor.Server.prototype, {
  subscribe: function (name, args, callback) {
    var self = this;
    var id;
    var existing = self.subs.find({name: name, args: args}, {reactive: false}).fetch();

    if (existing && existing[0]) {
      // already subbed, inc count.
      id = existing[0]._id;
      self.subs.update({_id: id}, {$inc: {count: 1}});

      if (callback) {
        if (self.sub_ready_callbacks[id])
          self.sub_ready_callbacks[id].push(callback);
        else
          callback(); // XXX maybe _.defer?
      }
    } else {
      // new sub, add object.
      // generate our own id so we can know it w/ a find afterwards.
      id = Collection.uuid();
      self.subs.insert({_id: id, name: name, args: args, count: 1});

      self.sub_ready_callbacks[id] = [];

      if (callback)
        self.sub_ready_callbacks[id].push(callback);
    }

    // return an object with a stop method.
    var token = {stop: function () {
      if (!id) return; // must have an id (local from above).
      // just update the database. observe takes care of the rest.
      self.subs.update({_id: id}, {$inc: {count: -1}});
    }};

    if (Meteor._capture_subs)
      Meteor._capture_subs.push(token);

    return token;
  },

  methods: function (methods) {
    var self = this;
    _.each(methods, function (func, name) {
      if (self.method_handlers[name])
        throw new Error("A method named '" + name + "' is already defined");
      self.method_handlers[name] = func;
    });
  },

  call: function (name /*, arguments */) {
    return this.apply(name, Array.prototype.slice.call(arguments, 1));
  },

  apply: function (name, args) {
    var self = this;
    var handler = self.method_handlers[name];

    args = _.clone(args);
    var result_func = function () {};
    if (args.length && typeof args[args.length - 1] === "function")
      result_func = args.pop();

    if (handler) {
      // run locally (if available)
      var local_args = _.clone(args);
      local_args.unshift(self.user_id);
      var ret = handler.apply(null, args);
    }

    // run on server
    self._send_method(
      {msg: 'method', method: name, params: args},
      result_func);
  },

  _send_method: function (msg, result_func) {
    var self = this;
    var method_id = self.next_method_id++;
    var new_msg = _.extend({id: method_id}, msg);
    self.pending_method_messages[method_id] = new_msg;
    self.pending_method_callbacks[method_id] = result_func || function () {};
    self.unsatisfied_methods[method_id] = true;

    self.stream.send(JSON.stringify(new_msg));
  },

  _livedata_connected: function (msg) {
    var self = this;
    // Meteor._debug("CONNECTED", msg);
  },

  _livedata_data: function (msg) {
    var self = this;

    // Add the data message to the queue
    self.pending_data.push(msg);

    // If there are still method invocations in flight, stop
    _.each(msg.methods || [], function (method_id) {
      delete self.unsatisfied_methods[method_id];
    });
    for (var method_id in self.unsatisfied_methods)
      return;

    // All methods have landed. Blow away local changes and replace
    // with authoritative changes from server.

    _.each(self.collections, function (coll) {
      if (coll._was_snapshot) {
        coll._collection.restore(); // Revert all local changes
        coll._was_snapshot = false;
      }
    });

    _.each(self.pending_data, function (msg) {
      if (msg.collection && msg.id) {
        var meteor_coll = self.collections[msg.collection];

        if (!meteor_coll) {
          Meteor._debug(
            "discarding data received for unknown collection " +
              JSON.stringify(msg.collection));
          return;
        }

        // do all the work against underlying minimongo collection.
        var coll = meteor_coll._collection;

        var doc = coll.findOne(msg.id);

        if (doc
            && (!msg.set || msg.set.length === 0)
            && _.difference(_.keys(doc), msg.unset, ['_id']).length === 0) {
          // what's left is empty, just remove it.  cannot fail.
          coll.remove(msg.id);
        } else if (doc) {
          var mutator = {$set: msg.set, $unset: {}};
          _.each(msg.unset, function (propname) {
            mutator.$unset[propname] = 1;
          });
          // XXX error check return value from update.
          coll.update(msg.id, mutator);
        } else {
          // XXX error check return value from insert.
          coll.insert(_.extend({_id: msg.id}, msg.set));
        }
      }

      if (msg.subs) {
        _.each(msg.subs, function (id) {
          var arr = self.sub_ready_callbacks[id];
          if (arr) _.each(arr, function (c) { c(); });
          delete self.sub_ready_callbacks[id];
        });
      }
    });

    self.pending_data = [];
  },

  _livedata_nosub: function (msg) {
    var self = this;
    // Meteor._debug("NOSUB", msg);
  },

  _livedata_result: function (msg) {
    var self = this;
    // id, result or error. error has error (code), reason, details

    if (('id' in msg) && (msg.id in self.pending_method_messages)) {
      delete self.pending_method_messages[msg.id];
    } else {
      // XXX write a better error
      Meteor._debug("Can't interpret method response message");
    }

    if (('id' in msg) && (msg.id in self.pending_method_callbacks)) {
      var func = self.pending_method_callbacks[msg.id];
      delete self.pending_method_callbacks[msg.id];
      // XXX wrap in try..catch?
      if ('error' in msg)
        func(msg.error);
      else
        func(undefined, msg.result);
    } else {
      // If a response is to a method we sent from reload pending methods,
      // it won't have a callback. This is OK. This will get revisited
      // when we do more intellegent auto-reloads where we wait to
      // quiesce, pre-stage cached assets, etc.
    }
  }
});

Meteor._Collection = function (name, server) {
  var self = this;

  if (name && (name in server.collections))
    // maybe should just return server.collections[name]?
    throw new Error("There is already a remote collection '" + name + "'");

  self._name = name;
  self._collection = new Collection;
  self._server = server;
  self._was_snapshot = false;

  if (name)
    server.collections[name] = self;
};

_.extend(Meteor._Collection.prototype, {
  find: function (/* selector, options */) {
    var self = this;
    // Collection.find() (return all docs) behaves differently
    // from Collection.find(undefined) (return 0 docs).  so be
    // careful about preserving the length of arguments when
    // descending into minimongo.
    return self._collection.find.apply(self._collection, Array.prototype.slice.call(arguments));
  },

  findOne: function (/* selector, options */) {
    var self = this;
    // as above
    return self._collection.findOne.apply(self._collection, Array.prototype.slice.call(arguments));
  },

  _maybe_snapshot: function () {
    var self = this;
    if (!self._was_snapshot) {
      self._collection.snapshot();
      self._was_snapshot = true;
    }
  },

  // XXX provide a way for the caller to find out about errors from the server?
  insert: function (obj) {
    var self = this;
    // Generate an id for the object.
    // XXX mutates the object passed in. that is not cool.
    if (obj._id)
      Meteor._debug("WARNING: trying to insert object w/ _id set. _id ignored.");
    if (_.keys(obj).length === 0)
      Meteor._debug("WARNING: inserting empty object.");

    var _id = Collection.uuid();
    obj._id = _id;

    if (self._name) {
      self._maybe_snapshot();
      self._server._send_method({
        msg: 'method',
        method: '/' + self._name + '/insert',
        params: [obj]});
    }
    self._collection.insert(obj);

    return obj;
  },

  // XXX provide a way for the caller to find out about errors from the server?
  update: function (selector, mutator, options) {
    var self = this;
    if (self._name) {
      self._maybe_snapshot();
      self._server._send_method({
        msg: 'method',
        method: '/' + self._name + '/update',
        params: [selector, mutator, options]});
    }
    self._collection.update(selector, mutator, options);
  },

  // XXX provide a way for the caller to find out about errors from the server?
  remove: function (selector) {
    var self = this;

    if (arguments.length === 0)
      selector = {};

    if (self._name) {
      self._maybe_snapshot();
      self._server._send_method({
        msg: 'method',
        method: '/' + self._name + '/remove',
        params: [selector]});
    }
    self._collection.remove(selector);
  },

  schema: function  () {
    // XXX not implemented yet
  }
});

// Path matches sockjs 'prefix' in stream_server. We should revisit this
// once we specify the 'on the wire' aspects of livedata more clearly.
App = new Meteor.Server('/sockjs');

_.extend(Meteor, {
  is_server: false,
  is_client: true,

  // XXX these are wrong
  status: function () {
    return App.stream.status();
  },

  reconnect: function () {
    return App.stream.reconnect();
  },

  publish: function() {
    // ignored on the client
  },

  // XXX make the user create it directly, with 'new'
  Collection: function (name) {
    return new Meteor._Collection(name, App);
  },

  subscribe: function (/* arguments */) {
    return App.subscribe.apply(App, _.toArray(arguments));
  },

  autosubscribe: function (sub_func) {
    var local_subs = [];
    var context = new Meteor.deps.Context();

    context.on_invalidate(function () {
      // recurse.
      Meteor.autosubscribe(sub_func);
      // unsub after re-subbing, to avoid bouncing.
      _.each(local_subs, function (x) { x.stop() });
    });

    context.run(function () {
      if (Meteor._capture_subs)
        throw new Error("Meteor.autosubscribe may not be called recursively");

      Meteor._capture_subs = [];
      try {
        sub_func();
      } finally {
        local_subs = Meteor._capture_subs;
        Meteor._capture_subs = null;
      }
    });
  }
});
<|endoftext|>


<commit_before>// constant indices into each array element of data
var COLOR = 0;
var PROX_START = 1;
var PROX_END = 2;
var DIST_START = 3;
var DIST_END = 4;

var color_scale = 20;

var COARSE_CUTOFF = 10000000;  // anything with a dimension smaller than this will be filtered

var genome_length = chrom_offsets[chrom_offsets.length-1];

var chart_height = 1000;
var chart_width = 1500;

var margin = 17;

var chart = d3.select(".chart")
    .attr("width", chart_width)
    .attr("height", chart_height);

var chart_group = chart.append("g");

var chrom_group = chart_group.append("g").attr("id", "chrom_group");
var unique_group = chart_group.append("g").attr("id", "unique_group");
var x_axis = chart_group.append("g").attr("id", "x-axis");
var y_axis = chart_group.append("g").attr("id", "y-axis");

var coarse_data = all_data.filter( function (d) {
    return (d[PROX_END] - d[PROX_START] > COARSE_CUTOFF) && (d[DIST_END] - d[DIST_START] > COARSE_CUTOFF)
});

function dataForChromPair(prox_target, dist_target) {
    return all_data.filter(function (d) {
            return (d[PROX_START] >= chrom_offsets[prox_target] && d[PROX_END] <= chrom_offsets[prox_target+1] &&
                d[DIST_START] >= chrom_offsets[dist_target] && d[DIST_END] <= chrom_offsets[dist_target+1]);
            });
}

function unflattenIndex(k) {
    return [Math.floor(k / chrom_sizes.length), k % chrom_sizes.length];
}

var chrom_pair_offsets = [];
// initialize data structure for each chrom pair
// first two dimensions are flattened
for (var i = 0; i < chrom_sizes.length; i++) {
    for (var j = 0; j < chrom_sizes.length; j++) {
        chrom_pair_offsets.push([chrom_offsets[i], chrom_offsets[j]]);
    }
}

function hexColorString(num) {
    var color = "00000" + num.toString(16);
    return "#" + color.substr(color.length - 6);
}

function rgb(num) {
    return [num >> 24, (num & 0x00ff00) >> 12, num & 0x0000ff];
}

function rgbColorString(r, g, b) {
    return "rgb(" + r + "," + g + "," + b + ")";
}

function scale(pos) {
    return (pos * (chart_height - margin)) / genome_length;
}

function translate(pos) {
    return (pos * (chart_height - margin)) / genome_length + margin;
}

function drawChroms() {
    return chrom_group.selectAll("rect")
        .data(chrom_pair_offsets)
        .enter().append("rect")
        .attr("transform", function (d) {
            return "translate(" + translate(d[0]) + "," + translate(d[1]) + ")";
        })
        .attr("fill", "white")
        .on('mouseover', function (d, k) {
            d3.select(this).attr("stroke", "black");
            var chrom_indices = unflattenIndex(k);
            chart_group.select("#x-axis > g:nth-child(" + (chrom_indices[0]+1) + ") > rect")
                .attr("fill", "lightgrey");
            chart_group.select("#y-axis > g:nth-child(" + (chrom_indices[1]+1) + ") > rect")
                .attr("fill", "lightgrey");
        })
        .on('mouseout', function (d, k) {
            d3.select(this).attr("stroke", null);
            var chrom_indices = unflattenIndex(k);
            chart_group.select("#x-axis > g:nth-child(" + (chrom_indices[0]+1) + ") > rect")
                .attr("fill", "None");
            chart_group.select("#y-axis > g:nth-child(" + (chrom_indices[1]+1) + ") > rect")
                .attr("fill", "None");
        })
        .on('click', function (d, k) {
            var chrom_indices = unflattenIndex(k);
            zoomToChromPair(chrom_indices[0], chrom_indices[1]);
        })
        .attr("width", function (d, k) {
            var chrom_indices = unflattenIndex(k);
            return scale(chrom_sizes[chrom_indices[0]]);
        })
        .attr("height", function (d, k) {
            var chrom_indices = unflattenIndex(k);
            return scale(chrom_sizes[chrom_indices[1]]);
        });
}

function colorChroms() {
    chrom_group.selectAll("rect")
        .attr("fill", function (d, k) {
            var areas = unique_areas[k];
            var chrom_indices = unflattenIndex(k);
            var chrom_area = chrom_sizes[chrom_indices[0]] * chrom_sizes[chrom_indices[1]];
            var max_color = null;
            var max_area = 0;
            var total_unique_area = 0;
            for (var color in areas) {
                if (areas.hasOwnProperty(color)) {
                    var area = areas[color];
                    total_unique_area += area;
                    if (area >= max_area) {
                        max_area = area;
                        max_color = color;
                    }
                }
            }
            var max_rgb = rgb(max_color);
            var intensity = 2 / (1 + Math.exp(-1 * color_scale * total_unique_area / chrom_area)) - 1;
            var whiteness = Math.round((1 - intensity) * 0xff);
            max_rgb = max_rgb.map(function (c) {
                return Math.round(c * intensity)
            });
            return rgbColorString(max_rgb[0] + whiteness, max_rgb[1] + whiteness, max_rgb[2] + whiteness);
        });
}

function drawAxes() {
    var groups = x_axis.selectAll("text")
        .data(chrom_names)
        .enter().append("g")
        .attr("transform", function (d, i) {
            return "translate(" + translate(chrom_offsets[i]) + ",0)";
        });
    groups.append("rect")
        .attr("height", margin)
        .attr("width", function (d, i) {
            return scale(chrom_sizes[i])
        })
        .attr("fill", "None");
    groups.append("text")
        .text(function (d) {return d;})
        .attr("alignment-baseline", "before-edge")
        .attr("text-anchor", "middle")
        .attr("transform", function (d, i) {
            return "translate(" + scale(chrom_sizes[i] / 2) + ",0)";
        });
    groups = y_axis.selectAll("text")
        .data(chrom_names)
        .enter().append("g")
        .attr("transform", function (d, i) {
            return "translate(0," + translate(chrom_offsets[i]) + ")";
        });
    groups.append("rect")
        .attr("width", margin)
        .attr("height", function (d, i) {
            return scale(chrom_sizes[i])
        })
        .attr("fill", "None");
    groups.append("text")
        .text(function (d) {return d;})
        .attr("transform", function (d, i) {
            return "translate(0," + scale(chrom_sizes[i] / 2) + ")";
        });
}

function drawUniquities(data) {
    var uniquities = unique_group.selectAll("rect")
        .data(data, function (d) { return d[PROX_START].toString() + d[DIST_START]});
    uniquities.exit().remove();
    uniquities.enter().append("rect")
        .attr("transform", function (d) {
            return "translate(" + translate(d[PROX_START]) + "," + translate(d[DIST_START]) + ")";
        })
        .attr("width", function (d) {
            return scale(d[PROX_END] - d[PROX_START]);
        })
        .attr("height", function (d) {
            return scale(d[DIST_END] - d[DIST_START]);
        })
        .attr("fill", function (d) {
            return hexColorString(d[COLOR]);
        })
}

function zoomToChromPair(i, j) {
    $("#slider").hide();
    $("#zoomout").show();
    chromo_rect.attr("display", "None");
    drawUniquities(dataForChromPair(i, j));
    var zoom_scale = Math.min(genome_length / chrom_sizes[i],
        genome_length / chrom_sizes[j]);
    var x = -translate(chrom_offsets[i]) * zoom_scale;
    var y = -translate(chrom_offsets[j]) * zoom_scale;
    chart_group.attr("transform",
        "translate(" + x + "," + y + ")scale(" + zoom_scale + ")");
    console.log(x);
    var zoom = d3.behavior.zoom()
        .xExtent([x, x + chrom_sizes[i]])// TODO: fix xExtent, yExtent
        .yExtent([y, y + chrom_sizes[j]])
        .scaleExtent([zoom_scale, 10000])
        .translate([x, y])
        .scale(zoom_scale);
    chart.call(zoom.on("zoom", function () {
        chart_group.attr("transform", "translate(" + d3.event.translate + ")" + "scale(" + d3.event.scale + ")")
    }));
}

drawUniquities(coarse_data);
var chromo_rect = drawChroms();
//colorChroms();
drawAxes();

$("#zoomout").hide().click( function () {
        $("#slider").show();
        $("#zoomout").hide();
        chromo_rect.attr("display", null);
        chart_group.attr("transform", null);
        var zoom = d3.behavior.zoom().on('zoom', null);
        chart.call(zoom.on("zoom", null));
        drawUniquities(coarse_data);
    }
);

//$("#slider").slider(
//    {
//        value:color_scale,
//        min:1,
//        max:10000,
//        step:10,
//        slide: function(event) {
//            color_scale = $("#slider").slider("value");
//            colorChroms();
//        }
//    }
//);
<commit_msg>Fix rgb method<commit_after>// constant indices into each array element of data
var COLOR = 0;
var PROX_START = 1;
var PROX_END = 2;
var DIST_START = 3;
var DIST_END = 4;

var color_scale = 20;

var COARSE_CUTOFF = 10000000;  // anything with a dimension smaller than this will be filtered

var genome_length = chrom_offsets[chrom_offsets.length-1];

var chart_height = 1000;
var chart_width = 1500;

var margin = 17;

var chart = d3.select(".chart")
    .attr("width", chart_width)
    .attr("height", chart_height);

var chart_group = chart.append("g");

var chrom_group = chart_group.append("g").attr("id", "chrom_group");
var unique_group = chart_group.append("g").attr("id", "unique_group");
var x_axis = chart_group.append("g").attr("id", "x-axis");
var y_axis = chart_group.append("g").attr("id", "y-axis");

var coarse_data = all_data.filter( function (d) {
    return (d[PROX_END] - d[PROX_START] > COARSE_CUTOFF) && (d[DIST_END] - d[DIST_START] > COARSE_CUTOFF)
});

function dataForChromPair(prox_target, dist_target) {
    return all_data.filter(function (d) {
            return (d[PROX_START] >= chrom_offsets[prox_target] && d[PROX_END] <= chrom_offsets[prox_target+1] &&
                d[DIST_START] >= chrom_offsets[dist_target] && d[DIST_END] <= chrom_offsets[dist_target+1]);
            });
}

function unflattenIndex(k) {
    return [Math.floor(k / chrom_sizes.length), k % chrom_sizes.length];
}

var chrom_pair_offsets = [];
// initialize data structure for each chrom pair
// first two dimensions are flattened
for (var i = 0; i < chrom_sizes.length; i++) {
    for (var j = 0; j < chrom_sizes.length; j++) {
        chrom_pair_offsets.push([chrom_offsets[i], chrom_offsets[j]]);
    }
}

function hexColorString(num) {
    var color = "00000" + num.toString(16);
    return "#" + color.substr(color.length - 6);
}

function rgb(num) {
    return [num >> 16, (num & 0x00ff00) >> 8, num & 0x0000ff];
}

function rgbColorString(r, g, b) {
    return "rgb(" + r + "," + g + "," + b + ")";
}

function scale(pos) {
    return (pos * (chart_height - margin)) / genome_length;
}

function translate(pos) {
    return (pos * (chart_height - margin)) / genome_length + margin;
}

function drawChroms() {
    return chrom_group.selectAll("rect")
        .data(chrom_pair_offsets)
        .enter().append("rect")
        .attr("transform", function (d) {
            return "translate(" + translate(d[0]) + "," + translate(d[1]) + ")";
        })
        .attr("fill", "white")
        .on('mouseover', function (d, k) {
            d3.select(this).attr("stroke", "black");
            var chrom_indices = unflattenIndex(k);
            chart_group.select("#x-axis > g:nth-child(" + (chrom_indices[0]+1) + ") > rect")
                .attr("fill", "lightgrey");
            chart_group.select("#y-axis > g:nth-child(" + (chrom_indices[1]+1) + ") > rect")
                .attr("fill", "lightgrey");
        })
        .on('mouseout', function (d, k) {
            d3.select(this).attr("stroke", null);
            var chrom_indices = unflattenIndex(k);
            chart_group.select("#x-axis > g:nth-child(" + (chrom_indices[0]+1) + ") > rect")
                .attr("fill", "None");
            chart_group.select("#y-axis > g:nth-child(" + (chrom_indices[1]+1) + ") > rect")
                .attr("fill", "None");
        })
        .on('click', function (d, k) {
            var chrom_indices = unflattenIndex(k);
            zoomToChromPair(chrom_indices[0], chrom_indices[1]);
        })
        .attr("width", function (d, k) {
            var chrom_indices = unflattenIndex(k);
            return scale(chrom_sizes[chrom_indices[0]]);
        })
        .attr("height", function (d, k) {
            var chrom_indices = unflattenIndex(k);
            return scale(chrom_sizes[chrom_indices[1]]);
        });
}

function colorChroms() {
    chrom_group.selectAll("rect")
        .attr("fill", function (d, k) {
            var areas = unique_areas[k];
            var chrom_indices = unflattenIndex(k);
            var chrom_area = chrom_sizes[chrom_indices[0]] * chrom_sizes[chrom_indices[1]];
            var max_color = null;
            var max_area = 0;
            var total_unique_area = 0;
            for (var color in areas) {
                if (areas.hasOwnProperty(color)) {
                    var area = areas[color];
                    total_unique_area += area;
                    if (area >= max_area) {
                        max_area = area;
                        max_color = color;
                    }
                }
            }
            var max_rgb = rgb(max_color);
            var intensity = 2 / (1 + Math.exp(-1 * color_scale * total_unique_area / chrom_area)) - 1;
            var whiteness = Math.round((1 - intensity) * 0xff);
            max_rgb = max_rgb.map(function (c) {
                return Math.round(c * intensity)
            });
            return rgbColorString(max_rgb[0] + whiteness, max_rgb[1] + whiteness, max_rgb[2] + whiteness);
        });
}

function drawAxes() {
    var groups = x_axis.selectAll("text")
        .data(chrom_names)
        .enter().append("g")
        .attr("transform", function (d, i) {
            return "translate(" + translate(chrom_offsets[i]) + ",0)";
        });
    groups.append("rect")
        .attr("height", margin)
        .attr("width", function (d, i) {
            return scale(chrom_sizes[i])
        })
        .attr("fill", "None");
    groups.append("text")
        .text(function (d) {return d;})
        .attr("alignment-baseline", "before-edge")
        .attr("text-anchor", "middle")
        .attr("transform", function (d, i) {
            return "translate(" + scale(chrom_sizes[i] / 2) + ",0)";
        });
    groups = y_axis.selectAll("text")
        .data(chrom_names)
        .enter().append("g")
        .attr("transform", function (d, i) {
            return "translate(0," + translate(chrom_offsets[i]) + ")";
        });
    groups.append("rect")
        .attr("width", margin)
        .attr("height", function (d, i) {
            return scale(chrom_sizes[i])
        })
        .attr("fill", "None");
    groups.append("text")
        .text(function (d) {return d;})
        .attr("transform", function (d, i) {
            return "translate(0," + scale(chrom_sizes[i] / 2) + ")";
        });
}

function drawUniquities(data) {
    var uniquities = unique_group.selectAll("rect")
        .data(data, function (d) { return d[PROX_START].toString() + d[DIST_START]});
    uniquities.exit().remove();
    uniquities.enter().append("rect")
        .attr("transform", function (d) {
            return "translate(" + translate(d[PROX_START]) + "," + translate(d[DIST_START]) + ")";
        })
        .attr("width", function (d) {
            return scale(d[PROX_END] - d[PROX_START]);
        })
        .attr("height", function (d) {
            return scale(d[DIST_END] - d[DIST_START]);
        })
        .attr("fill", function (d) {
            return hexColorString(d[COLOR]);
        })
}

function zoomToChromPair(i, j) {
    $("#slider").hide();
    $("#zoomout").show();
    chromo_rect.attr("display", "None");
    drawUniquities(dataForChromPair(i, j));
    var zoom_scale = Math.min(genome_length / chrom_sizes[i],
        genome_length / chrom_sizes[j]);
    var x = -translate(chrom_offsets[i]) * zoom_scale;
    var y = -translate(chrom_offsets[j]) * zoom_scale;
    chart_group.attr("transform",
        "translate(" + x + "," + y + ")scale(" + zoom_scale + ")");
    console.log(x);
    var zoom = d3.behavior.zoom()
        .xExtent([x, x + chrom_sizes[i]])// TODO: fix xExtent, yExtent
        .yExtent([y, y + chrom_sizes[j]])
        .scaleExtent([zoom_scale, 10000])
        .translate([x, y])
        .scale(zoom_scale);
    chart.call(zoom.on("zoom", function () {
        chart_group.attr("transform", "translate(" + d3.event.translate + ")" + "scale(" + d3.event.scale + ")")
    }));
}

drawUniquities(coarse_data);
var chromo_rect = drawChroms();
//colorChroms();
drawAxes();

$("#zoomout").hide().click( function () {
        $("#slider").show();
        $("#zoomout").hide();
        chromo_rect.attr("display", null);
        chart_group.attr("transform", null);
        var zoom = d3.behavior.zoom().on('zoom', null);
        chart.call(zoom.on("zoom", null));
        drawUniquities(coarse_data);
    }
);

//$("#slider").slider(
//    {
//        value:color_scale,
//        min:1,
//        max:10000,
//        step:10,
//        slide: function(event) {
//            color_scale = $("#slider").slider("value");
//            colorChroms();
//        }
//    }
//);
<|endoftext|>


<commit_before>var _ = require('lodash');
var util = require('../../core/util.js');
var log = require(`${util.dirs().core}log`);

var handle = require('./handle');
var mongoUtil = require('./util');

var Reader = function Reader () {
  _.bindAll(this);
  this.db = handle;
  this.collection = this.db.collection(mongoUtil.settings.historyCollection);
  this.pair = mongoUtil.settings.pair.join('_');
}

// returns the furtherst point (up to `from`) in time we have valid data from
Reader.prototype.mostRecentWindow = function mostRecentWindow (from, to, next) {
  var mFrom = from.unix();
  var mTo = to.unix();

  var maxAmount = to - from + 1;

  // Find some documents
  this.collection.find({ pair: this.pair, start: { $gte: mFrom, $lte: mTo } }).sort({ start: 1 }).toArray((err, docs) => {
    if (err) {
      return util.die('DB error at `mostRecentWindow`');
    }
    // no candles are available
    if (docs.length === 0) {
      log.debug('no candles are available');
      return next(false);
    }

    if (docs.length === maxAmount) {
      // full history is available!
      log.debug('full history is available!');
      return next({
        mFrom,
        mTo
      });
    }

    // we have at least one gap, figure out where
    var mostRecent = _.first(docs).start;

    var gapIndex = _.findIndex(docs, (r, i) => r.start !== mostRecent - i * 60);

    // if there was no gap in the records, but
    // there were not enough records.
    if (gapIndex === -1) {
      var leastRecent = _.last(docs).start;
      return next({
        from: leastRecent,
        to: mostRecent
      });
    }

    // else return mostRecent and the
    // the minute before the gap
    return next({
      from: docs[gapIndex - 1].start,
      to: mostRecent
    });
  })
}

Reader.prototype.get = function get (from, to, what, next) { // returns all fields in general
  // Find some documents
  this.collection.find({ pair: this.pair, start: { $gte: from, $lte: to } }).sort({ start: 1 }).toArray((err, docs) => {
    if (err) {
      return util.die('DB error at `get`');
    }
    return next(null, docs);
  });
}

Reader.prototype.count = function fCount (from, to, next) {
  this.collection.count({ pair: this.pair, start: { $gte: from, $lte: to } }, (err, count) => {
    if (err) {
      return util.die('DB error at `count`');
    }
    return next(null, count);
  })
}

Reader.prototype.countTotal = function countTotal (next) {
  this.collection.count({ pair: this.pair }, (err, count) => {
    if (err) {
      return util.die('DB error at `countTotal`');
    }
    return next(null, count);
  })
}

Reader.prototype.getBoundry = function getBoundry (next) {
  this.collection.find({ pair: this.pair }, { start: 1 }).sort({ start: 1 }).limit(1).toArray((err, docs) => {
    if (err) {
      return util.die('DB error at `getBoundry`');
    }
    var start = _.first(docs).start;

    collection.find({ pair: this.pair }, { start: 1 }).sort({ start: -1 }).limit(1).toArray((err2, docs2) => {
      if (err2) {
        return util.die('DB error at `getBoundry`');
      }
      var end = _.first(docs2).start;
      return next(null, { first: start, last: end });
    });
    return null;
  });
}

Reader.prototype.close = function close () {
  this.db = null;
}

module.exports = Reader;
<commit_msg>Add tableExists for mongodb<commit_after>var _ = require('lodash');
var util = require('../../core/util.js');
var log = require(`${util.dirs().core}log`);

var handle = require('./handle');
var mongoUtil = require('./util');

var Reader = function Reader () {
  _.bindAll(this);
  this.db = handle;
  this.collection = this.db.collection(mongoUtil.settings.historyCollection);
  this.pair = mongoUtil.settings.pair.join('_');
}

// returns the furtherst point (up to `from`) in time we have valid data from
Reader.prototype.mostRecentWindow = function mostRecentWindow (from, to, next) {
  var mFrom = from.unix();
  var mTo = to.unix();

  var maxAmount = to - from + 1;

  // Find some documents
  this.collection.find({ pair: this.pair, start: { $gte: mFrom, $lte: mTo } }).sort({ start: 1 }, (err, docs) => {
    if (err) {
      return util.die('DB error at `mostRecentWindow`');
    }
    // no candles are available
    if (docs.length === 0) {
      log.debug('no candles are available');
      return next(false);
    }

    if (docs.length === maxAmount) {
      // full history is available!
      log.debug('full history is available!');
      return next({
        mFrom,
        mTo
      });
    }

    // we have at least one gap, figure out where
    var mostRecent = _.first(docs).start;

    var gapIndex = _.findIndex(docs, (r, i) => r.start !== mostRecent - i * 60);

    // if there was no gap in the records, but
    // there were not enough records.
    if (gapIndex === -1) {
      var leastRecent = _.last(docs).start;
      return next({
        from: leastRecent,
        to: mostRecent
      });
    }

    // else return mostRecent and the
    // the minute before the gap
    return next({
      from: docs[gapIndex - 1].start,
      to: mostRecent
    });
  })
}

Reader.prototype.get = function get (from, to, what, next) { // returns all fields in general
  // Find some documents
  this.collection.find({ pair: this.pair, start: { $gte: from, $lte: to } }).sort({ start: 1 }, (err, docs) => {
    if (err) {
      return util.die('DB error at `get`');
    }
    return next(null, docs);
  });
}

Reader.prototype.count = function fCount (from, to, next) {
  this.collection.count({ pair: this.pair, start: { $gte: from, $lte: to } }, (err, count) => {
    if (err) {
      return util.die('DB error at `count`');
    }
    return next(null, count);
  })
}

Reader.prototype.countTotal = function countTotal (next) {
  this.collection.count({ pair: this.pair }, (err, count) => {
    if (err) {
      return util.die('DB error at `countTotal`');
    }
    return next(null, count);
  })
}

Reader.prototype.getBoundry = function getBoundry (next) {
  this.collection.find({ pair: this.pair }, { start: 1 }).sort({ start: 1 }).limit(1, (err, docs) => {
    if (err) {
      return util.die('DB error at `getBoundry`');
    }
    var start = _.first(docs).start;

    collection.find({ pair: this.pair }, { start: 1 }).sort({ start: -1 }).limit(1, (err2, docs2) => {
      if (err2) {
        return util.die('DB error at `getBoundry`');
      }
      var end = _.first(docs2).start;
      return next(null, { first: start, last: end });
    });
    return null;
  });
}

Reader.prototype.tableExists = function(name, next) {
  this.db.getCollectionNames((err, names) => {
    if (err) {
      log.debug(err);
      return util.die('DB error at `tableExists`');
    }
    next(null, names.indexOf(name) > -1);
  });
}

Reader.prototype.close = function close () {
  this.db = null;
}

module.exports = Reader;
<|endoftext|>


<commit_before>(function() {
'use strict';

angular.module('ionic.ui.viewState', ['ionic.service.view', 'ionic.service.gesture', 'ngSanitize'])

/**
 * @ngdoc directive
 * @name ionView
 * @module ionic
 * @restrict E
 * @parent ionNavView
 *
 * @description
 * A container for content, used to tell a parent {@link ionic.directive:ionNavBar}
 * about the current view.
 *
 * @usage
 * Below is an example where our page will load with a navbar containing "My Page" as the title.
 *
 * ```html
 * <ion-nav-bar></ion-nav-bar>
 * <ion-nav-view class="slide-left-right">
 *   <ion-view title="My Page">
 *     <ion-content>
 *       Hello!
 *     </ion-content>
 *   </ion-view>
 * </ion-nav-view>
 * ```
 *
 * @param {string=} title The title to display on the parent {@link ionic.directive:ionNavBar}.
 * @param {boolean=} hideBackButton Whether to hide the back button on the parent
 * {@link ionic.directive:ionNavBar} by default.
 * @param {boolean=} hideNavBar Whether to hide the parent
 * {@link ionic.directive:ionNavBar} by default.
 */
.directive('ionView', ['$ionicViewService', '$rootScope', '$animate',
           function( $ionicViewService,   $rootScope,   $animate) {
  return {
    restrict: 'EA',
    priority: 1000,
    require: '^?ionNavBar',
    compile: function(tElement, tAttrs, transclude) {
      tElement.addClass('pane');
      tElement[0].removeAttribute('title');

      return function link($scope, $element, $attr, navBarCtrl) {
        if (!navBarCtrl) {
          return;
        }

        if (angular.isDefined($attr.title)) {

          var initialTitle = $attr.title;
          navBarCtrl.changeTitle(initialTitle, $scope.$navDirection);

          // watch for changes in the title, don't set initial value as changeTitle does that
          $attr.$observe('title', function(val, oldVal) {
            if (val !== initialTitle) {
              navBarCtrl.setTitle(val);
            }
          });

        }

        $scope.$watch($attr.hideBackButton, function(value) {
          // Should we hide a back button when this tab is shown
          navBarCtrl.showBackButton(!value);
        });

        $scope.$watch($attr.hideNavBar, function(value) {
          // Should the nav bar be hidden for this view or not?
          navBarCtrl.showBar(!value);
        });

      };
    }
  };
}])

/**
 * @ngdoc directive
 * @name ionNavView
 * @module ionic
 * @restrict E
 * @codepen HjnFx
 *
 * @description
 * As a user navigates throughout your app, Ionic is able to keep track of their
 * navigation history. By knowing their history, transitions between views
 * correctly slide either left or right, or no transition at all. An additional
 * benefit to Ionic's navigation system is its ability to manage multiple
 * histories.
 *
 * Ionic uses the AngularUI Router module so app interfaces can be organized
 * into various "states". Like Angular's core $route service, URLs can be used
 * to control the views. However, the AngularUI Router provides a more powerful
 * state manager in that states are bound to named, nested, and parallel views,
 * allowing more than one template to be rendered on the same page.
 * Additionally, each state is not required to be bound to a URL, and data can
 * be pushed to each state which allows much flexibility.
 *
 * The ionNavView directive is used to render templates in your application. Each template
 * is part of a state. States are usually mapped to a url, and are defined programatically
 * using angular-ui-router (see [their docs](https://github.com/angular-ui/ui-router/wiki)),
 * and remember to replace ui-view with ion-nav-view in examples).
 *
 * @usage
 * In this example, we will create a navigation view that contains our different states for the app.
 *
 * To do this, in our markup we use ionNavView top level directive. To display a header bar we use
 * the {@link ionic.directive:ionNavBar} directive that updates as we navigate through the
 * navigation stack.
 *
 * You can any [animation class](/docs/components#animation) on the navView to have its pages slide.
 * Recommended for page transitions: 'slide-left-right', 'slide-left-right-ios7', 'slide-in-up'.
 *
 * ```html
 * <ion-nav-bar></ion-nav-bar>
 * <ion-nav-view class="slide-left-right">
 *   <!-- Center content -->
 * </ion-nav-view>
 * ```
 *
 * Next, we need to setup our states that will be rendered.
 *
 * ```js
 * var app = angular.module('myApp', ['ionic']);
 * app.config(function($stateProvider) {
 *   $stateProvider
 *   .state('index', {
 *     url: '/',
 *     templateUrl: 'home.html'
 *   })
 *   .state('music', {
 *     url: '/music',
 *     templateUrl: 'music.html'
 *   });
 * });
 * ```
 * Then on app start, $stateProvider will look at the url, see it matches the index state,
 * and then try to load home.html into the `<ion-nav-view>`.
 *
 * Pages are loaded by the URLs given. One simple way to create templates in Angular is to put
 * them directly into your HTML file and use the `<script type="text/ng-template">` syntax.
 * So here is one way to put home.html into our app:
 *
 * ```html
 * <script id="home" type="text/ng-template">
 *   <!-- The title of the ion-view will be shown on the navbar -->
 *   <ion-view title="'Home'">
 *     <ion-content ng-controller="HomeCtrl">
 *       <!-- The content of the page -->
 *       <a href="#/music">Go to music page!</a>
 *     </ion-content>
 *   </ion-view>
 * </script>
 * ```
 *
 * This is good to do because the template will be cached for very fast loading, instead of
 * having to fetch them from the network.
 *
 * Please visit [AngularUI Router's docs](https://github.com/angular-ui/ui-router/wiki) for
 * more info. Below is a great video by the AngularUI Router guys that may help to explain
 * how it all works:
 *
 *
 *
 * @param {string=} name A view name. The name should be unique amongst the other views in the
 * same state. You can have views of the same name that live in different states. For more
 * information, see ui-router's [ui-view documentation](http://angular-ui.github.io/ui-router/site/#/api/ui.router.state.directive:ui-view).
 */
.directive('ionNavView', ['$ionicViewService', '$state', '$compile', '$controller', '$animate',
              function( $ionicViewService,   $state,   $compile,   $controller,   $animate) {
  // IONIC's fork of Angular UI Router, v0.2.7
  // the navView handles registering views in the history, which animation to use, and which
  var viewIsUpdating = false;

  var directive = {
    restrict: 'E',
    terminal: true,
    priority: 2000,
    transclude: true,
    controller: [function(){
    }],
    compile: function (element, attr, transclude) {
      return function(scope, element, attr, navViewCtrl) {
        var viewScope, viewLocals,
            name = attr[directive.name] || attr.name || '',
            onloadExp = attr.onload || '',
            initialView = transclude(scope);

        // Put back the compiled initial view
        element.append(initialView);

        // Find the details of the parent view directive (if any) and use it
        // to derive our own qualified view name, then hang our own details
        // off the DOM so child directives can find it.
        var parent = element.parent().inheritedData('$uiView');
        if (name.indexOf('@') < 0) name  = name + '@' + (parent ? parent.state.name : '');
        var view = { name: name, state: null };
        element.data('$uiView', view);

        var eventHook = function() {
          if (viewIsUpdating) return;
          viewIsUpdating = true;

          try { updateView(true); } catch (e) {
            viewIsUpdating = false;
            throw e;
          }
          viewIsUpdating = false;
        };

        scope.$on('$stateChangeSuccess', eventHook);
        scope.$on('$viewContentLoading', eventHook);
        updateView(false);

        function updateView(doAnimate) {
          //===false because $animate.enabled() is a noop without angular-animate included
          if ($animate.enabled() === false) {
            doAnimate = false;
          }

          var locals = $state.$current && $state.$current.locals[name];
          if (locals === viewLocals) return; // nothing to do
          var renderer = $ionicViewService.getRenderer(element, attr, scope);

          // Destroy previous view scope
          if (viewScope) {
            viewScope.$destroy();
            viewScope = null;
          }

          if (!locals) {
            viewLocals = null;
            view.state = null;

            // Restore the initial view
            return element.append(initialView);
          }

          var newElement = angular.element('

').html(locals.$template).contents();
          var viewRegisterData = renderer().register(newElement);

          // Remove existing content
          renderer(doAnimate).leave();

          viewLocals = locals;
          view.state = locals.$$state;

          renderer(doAnimate).enter(newElement);

          var link = $compile(newElement);
          viewScope = scope.$new();

          viewScope.$navDirection = viewRegisterData.navDirection;

          if (locals.$$controller) {
            locals.$scope = viewScope;
            var controller = $controller(locals.$$controller, locals);
            element.children().data('$ngControllerController', controller);
          }
          link(viewScope);

          var viewHistoryData = $ionicViewService._getViewById(viewRegisterData.viewId) || {};
          viewScope.$broadcast('$viewContentLoaded', viewHistoryData);

          if (onloadExp) viewScope.$eval(onloadExp);

          newElement = null;
        }
      };
    }
  };
  return directive;
}])

/**
 * @ngdoc directive
 * @name navClear
 * @module ionic
 * @restrict AC
 *
 * @description
 * Disables any transition animations between views, along with removing the back
 * button which would normally show on the next view. This directive is useful for
 * links within a sideMenu.
 *
 * @usage
 * Below is an example of a link within a side menu. Tapping this link would disable
 * any animations which would normally occur between views.
 *
 * ```html
 * <a nav-clear menu-close href="#/home" class="item">Home</a>
 * ```
 */
.directive('navClear', ['$ionicViewService', function($ionicViewService) {
  return {
    restrict: 'AC',
    link: function($scope, $element, $attr) {
      $element.bind('click', function(){
        $ionicViewService.nextViewOptions({
          disableAnimate: true,
          disableBack: true
        });
      });
    }
  };
}]);

})();
<commit_msg>docs(ionNavView): correct typo<commit_after>(function() {
'use strict';

angular.module('ionic.ui.viewState', ['ionic.service.view', 'ionic.service.gesture', 'ngSanitize'])

/**
 * @ngdoc directive
 * @name ionView
 * @module ionic
 * @restrict E
 * @parent ionNavView
 *
 * @description
 * A container for content, used to tell a parent {@link ionic.directive:ionNavBar}
 * about the current view.
 *
 * @usage
 * Below is an example where our page will load with a navbar containing "My Page" as the title.
 *
 * ```html
 * <ion-nav-bar></ion-nav-bar>
 * <ion-nav-view class="slide-left-right">
 *   <ion-view title="My Page">
 *     <ion-content>
 *       Hello!
 *     </ion-content>
 *   </ion-view>
 * </ion-nav-view>
 * ```
 *
 * @param {string=} title The title to display on the parent {@link ionic.directive:ionNavBar}.
 * @param {boolean=} hideBackButton Whether to hide the back button on the parent
 * {@link ionic.directive:ionNavBar} by default.
 * @param {boolean=} hideNavBar Whether to hide the parent
 * {@link ionic.directive:ionNavBar} by default.
 */
.directive('ionView', ['$ionicViewService', '$rootScope', '$animate',
           function( $ionicViewService,   $rootScope,   $animate) {
  return {
    restrict: 'EA',
    priority: 1000,
    require: '^?ionNavBar',
    compile: function(tElement, tAttrs, transclude) {
      tElement.addClass('pane');
      tElement[0].removeAttribute('title');

      return function link($scope, $element, $attr, navBarCtrl) {
        if (!navBarCtrl) {
          return;
        }

        if (angular.isDefined($attr.title)) {

          var initialTitle = $attr.title;
          navBarCtrl.changeTitle(initialTitle, $scope.$navDirection);

          // watch for changes in the title, don't set initial value as changeTitle does that
          $attr.$observe('title', function(val, oldVal) {
            if (val !== initialTitle) {
              navBarCtrl.setTitle(val);
            }
          });

        }

        $scope.$watch($attr.hideBackButton, function(value) {
          // Should we hide a back button when this tab is shown
          navBarCtrl.showBackButton(!value);
        });

        $scope.$watch($attr.hideNavBar, function(value) {
          // Should the nav bar be hidden for this view or not?
          navBarCtrl.showBar(!value);
        });

      };
    }
  };
}])

/**
 * @ngdoc directive
 * @name ionNavView
 * @module ionic
 * @restrict E
 * @codepen HjnFx
 *
 * @description
 * As a user navigates throughout your app, Ionic is able to keep track of their
 * navigation history. By knowing their history, transitions between views
 * correctly slide either left or right, or no transition at all. An additional
 * benefit to Ionic's navigation system is its ability to manage multiple
 * histories.
 *
 * Ionic uses the AngularUI Router module so app interfaces can be organized
 * into various "states". Like Angular's core $route service, URLs can be used
 * to control the views. However, the AngularUI Router provides a more powerful
 * state manager in that states are bound to named, nested, and parallel views,
 * allowing more than one template to be rendered on the same page.
 * Additionally, each state is not required to be bound to a URL, and data can
 * be pushed to each state which allows much flexibility.
 *
 * The ionNavView directive is used to render templates in your application. Each template
 * is part of a state. States are usually mapped to a url, and are defined programatically
 * using angular-ui-router (see [their docs](https://github.com/angular-ui/ui-router/wiki),
 * and remember to replace ui-view with ion-nav-view in examples).
 *
 * @usage
 * In this example, we will create a navigation view that contains our different states for the app.
 *
 * To do this, in our markup we use ionNavView top level directive. To display a header bar we use
 * the {@link ionic.directive:ionNavBar} directive that updates as we navigate through the
 * navigation stack.
 *
 * You can any [animation class](/docs/components#animation) on the navView to have its pages slide.
 * Recommended for page transitions: 'slide-left-right', 'slide-left-right-ios7', 'slide-in-up'.
 *
 * ```html
 * <ion-nav-bar></ion-nav-bar>
 * <ion-nav-view class="slide-left-right">
 *   <!-- Center content -->
 * </ion-nav-view>
 * ```
 *
 * Next, we need to setup our states that will be rendered.
 *
 * ```js
 * var app = angular.module('myApp', ['ionic']);
 * app.config(function($stateProvider) {
 *   $stateProvider
 *   .state('index', {
 *     url: '/',
 *     templateUrl: 'home.html'
 *   })
 *   .state('music', {
 *     url: '/music',
 *     templateUrl: 'music.html'
 *   });
 * });
 * ```
 * Then on app start, $stateProvider will look at the url, see it matches the index state,
 * and then try to load home.html into the `<ion-nav-view>`.
 *
 * Pages are loaded by the URLs given. One simple way to create templates in Angular is to put
 * them directly into your HTML file and use the `<script type="text/ng-template">` syntax.
 * So here is one way to put home.html into our app:
 *
 * ```html
 * <script id="home" type="text/ng-template">
 *   <!-- The title of the ion-view will be shown on the navbar -->
 *   <ion-view title="'Home'">
 *     <ion-content ng-controller="HomeCtrl">
 *       <!-- The content of the page -->
 *       <a href="#/music">Go to music page!</a>
 *     </ion-content>
 *   </ion-view>
 * </script>
 * ```
 *
 * This is good to do because the template will be cached for very fast loading, instead of
 * having to fetch them from the network.
 *
 * Please visit [AngularUI Router's docs](https://github.com/angular-ui/ui-router/wiki) for
 * more info. Below is a great video by the AngularUI Router guys that may help to explain
 * how it all works:
 *
 *
 *
 * @param {string=} name A view name. The name should be unique amongst the other views in the
 * same state. You can have views of the same name that live in different states. For more
 * information, see ui-router's [ui-view documentation](http://angular-ui.github.io/ui-router/site/#/api/ui.router.state.directive:ui-view).
 */
.directive('ionNavView', ['$ionicViewService', '$state', '$compile', '$controller', '$animate',
              function( $ionicViewService,   $state,   $compile,   $controller,   $animate) {
  // IONIC's fork of Angular UI Router, v0.2.7
  // the navView handles registering views in the history, which animation to use, and which
  var viewIsUpdating = false;

  var directive = {
    restrict: 'E',
    terminal: true,
    priority: 2000,
    transclude: true,
    controller: [function(){
    }],
    compile: function (element, attr, transclude) {
      return function(scope, element, attr, navViewCtrl) {
        var viewScope, viewLocals,
            name = attr[directive.name] || attr.name || '',
            onloadExp = attr.onload || '',
            initialView = transclude(scope);

        // Put back the compiled initial view
        element.append(initialView);

        // Find the details of the parent view directive (if any) and use it
        // to derive our own qualified view name, then hang our own details
        // off the DOM so child directives can find it.
        var parent = element.parent().inheritedData('$uiView');
        if (name.indexOf('@') < 0) name  = name + '@' + (parent ? parent.state.name : '');
        var view = { name: name, state: null };
        element.data('$uiView', view);

        var eventHook = function() {
          if (viewIsUpdating) return;
          viewIsUpdating = true;

          try { updateView(true); } catch (e) {
            viewIsUpdating = false;
            throw e;
          }
          viewIsUpdating = false;
        };

        scope.$on('$stateChangeSuccess', eventHook);
        scope.$on('$viewContentLoading', eventHook);
        updateView(false);

        function updateView(doAnimate) {
          //===false because $animate.enabled() is a noop without angular-animate included
          if ($animate.enabled() === false) {
            doAnimate = false;
          }

          var locals = $state.$current && $state.$current.locals[name];
          if (locals === viewLocals) return; // nothing to do
          var renderer = $ionicViewService.getRenderer(element, attr, scope);

          // Destroy previous view scope
          if (viewScope) {
            viewScope.$destroy();
            viewScope = null;
          }

          if (!locals) {
            viewLocals = null;
            view.state = null;

            // Restore the initial view
            return element.append(initialView);
          }

          var newElement = angular.element('

').html(locals.$template).contents();
          var viewRegisterData = renderer().register(newElement);

          // Remove existing content
          renderer(doAnimate).leave();

          viewLocals = locals;
          view.state = locals.$$state;

          renderer(doAnimate).enter(newElement);

          var link = $compile(newElement);
          viewScope = scope.$new();

          viewScope.$navDirection = viewRegisterData.navDirection;

          if (locals.$$controller) {
            locals.$scope = viewScope;
            var controller = $controller(locals.$$controller, locals);
            element.children().data('$ngControllerController', controller);
          }
          link(viewScope);

          var viewHistoryData = $ionicViewService._getViewById(viewRegisterData.viewId) || {};
          viewScope.$broadcast('$viewContentLoaded', viewHistoryData);

          if (onloadExp) viewScope.$eval(onloadExp);

          newElement = null;
        }
      };
    }
  };
  return directive;
}])

/**
 * @ngdoc directive
 * @name navClear
 * @module ionic
 * @restrict AC
 *
 * @description
 * Disables any transition animations between views, along with removing the back
 * button which would normally show on the next view. This directive is useful for
 * links within a sideMenu.
 *
 * @usage
 * Below is an example of a link within a side menu. Tapping this link would disable
 * any animations which would normally occur between views.
 *
 * ```html
 * <a nav-clear menu-close href="#/home" class="item">Home</a>
 * ```
 */
.directive('navClear', ['$ionicViewService', function($ionicViewService) {
  return {
    restrict: 'AC',
    link: function($scope, $element, $attr) {
      $element.bind('click', function(){
        $ionicViewService.nextViewOptions({
          disableAnimate: true,
          disableBack: true
        });
      });
    }
  };
}]);

})();
<|endoftext|>


<commit_before>/**
 * https://github.com/facebook/react-native/blob/master/Libraries/Storage/AsyncStorage.js
 */

function wrap(value, callback) {
  return Promise.resolve(value).then(
    obj => callback(null, obj),
    err => callback(err)
  );
}

let db = {};

const AsyncStorage = {
  getItem(key, callback) {
    return wrap(db[key], callback);
  },

  setItem(key, value, callback) {
    db[key] = value;
    return wrap(null, callback);
  },

  removeItem(key, callback) {
    delete db[key];
    return wrap(null, callback);
  },

  mergeItem(key, value, callback) {
    db[key] = Object.assign({}, db[key] || {}, value);
    return wrap(null, callback);
  },

  clear(callback) {
    db = {};
    return wrap(null, callback);
  },

  getAllKeys(callback) {
    return wrap(Object.keys(db), callback);
  },

  flushGetRequests() {

  },

  multiGet(keys, callback) {
    return wrap(keys.map(k => [k, db[k]]), callback);
  },

  multiSet(keyValuePairs, callback) {
    keyValuePairs.forEach(([key, value]) => {
      db[key] = value;
    });
    return wrap(null, callback);
  },

  multiRemove(keys, callback) {
    keys.forEach(key => delete db[key]);
    return wrap(null, callback);
  },

  multiMerge(keyValuePairs, callback) {
    keyValuePairs.forEach(([key, value]) => {
      db[key] = Object.asign({}, db[key] || {}, value);
    });
    return wrap(null, callback);
  },
};

module.exports = AsyncStorage;
<commit_msg>Fixes AsyncStorage set/get item if no callback is provided<commit_after>/**
 * https://github.com/facebook/react-native/blob/master/Libraries/Storage/AsyncStorage.js
 */

function wrap(value, callback) {
  return Promise.resolve(value).then(
    obj => callback && callback(null, obj),
    err => callback && callback(err)
  );
}

let db = {};

const AsyncStorage = {
  getItem(key, callback) {
    return wrap(db[key], callback);
  },

  setItem(key, value, callback) {
    db[key] = value;
    return wrap(null, callback);
  },

  removeItem(key, callback) {
    delete db[key];
    return wrap(null, callback);
  },

  mergeItem(key, value, callback) {
    db[key] = Object.assign({}, db[key] || {}, value);
    return wrap(null, callback);
  },

  clear(callback) {
    db = {};
    return wrap(null, callback);
  },

  getAllKeys(callback) {
    return wrap(Object.keys(db), callback);
  },

  flushGetRequests() {

  },

  multiGet(keys, callback) {
    return wrap(keys.map(k => [k, db[k]]), callback);
  },

  multiSet(keyValuePairs, callback) {
    keyValuePairs.forEach(([key, value]) => {
      db[key] = value;
    });
    return wrap(null, callback);
  },

  multiRemove(keys, callback) {
    keys.forEach(key => delete db[key]);
    return wrap(null, callback);
  },

  multiMerge(keyValuePairs, callback) {
    keyValuePairs.forEach(([key, value]) => {
      db[key] = Object.asign({}, db[key] || {}, value);
    });
    return wrap(null, callback);
  },
};

module.exports = AsyncStorage;
<|endoftext|>