<commit_before>(function($) {
	"use strict";

	/**
	 * This namespace provides the models and collections for working with BC web applications and their items.
	 *
	 * @namespace BCAPI.Models.WebApp
	 * @memberOf BCAPI.Models
	 */
	BCAPI.Models.WebApp = {};

    /**
     * This class provides the model for interacting with web apps.
     *
     * ## Load webapps list
     *
     * ```javascript
     * var apps = new BCAPI.Models.WebApp.AppCollection();
     * apps.fetch({
     *		success: function(webAppItems) {
	 * 			// handle success
	 * 		},
	 * 		error: function(webAppItems, xhr) {
	 * 			// handle errors
	 * 		}
	 * });
     *
     * apps.each(function(webApp) {
	 * 		// display logic
	 * });
     * ```
     *
     * ## Create webapp
     *
     * ```javascript
     * var app = new BCAPI.Models.WebApp.App({
	 * 		"name": "Test app"
	 * });
     *
     * app.save({
	 * 		success: function(webAppItem) {
	 * 			// handle success
	 * 		},
	 * 		error: function(webAppItem, xhr) {
	 * 			// handle errors
	 * 		}
	 * });
     * ```
     *
     * If you want to refresh collections which rely on Item model please refresh those collections.
     *
     * ## Remove app
     *
     * ```javascript
     * var app = new BCAPI.Models.WebApp.App({name: "Test app"});
     * app.destroy({
	 * 	success: function(webAppItem, response) {
	 * 		// handle success here.
	 *  },
	 *  error: function(webAppItem, xhr, options) {
	 * 		// handle error scenario.
	 *  }
	 * });
     * ```
     *
     * ## Supported attributes
     *
     * ```javascript
     * var app = new BCAPI.Models.WebApp.App({
	 *	templateId: -1,
     *  uploadFolder: "images",
     *  requiresApproval: true,
     *  allowFileUpload: true,
     *  customerCanAdd: false,
     *  customerCanDelete: false,
     *  customerCanEdit: false,
     *  anyoneCanEdit: false,
     *  requiresPayment: false,
     *  validDays: -1, // never expire
     *  roleId: 0,
     *  hasAddress: false,
     *  disableDetailPages: false,
     *  locationEnabled: false
     * });
     * ```
     *
     * @class
     */
	BCAPI.Models.WebApp.App = BCAPI.Models.Model.extend({
        idAttribute: 'name',

        /**
         * Set tot true if you want to save or delete an existing item before fetching it
         *
         * @type {boolean}
         * @memberOf WebApp
         */
        isNotNew: null,

        isNew: function() {
            return this.isNotNew ? false : !this.get('id');
        },

        endpoint: function() {
            return '/api/v2/admin/sites/current/webapps';
        },

        fetch: function() {
            this.isNotNew = true;
            return Backbone.Model.prototype.fetch.apply(this, arguments);
        },

        destroy: function() {
            this.isNotNew = true;
            return Backbone.Model.prototype.destroy.apply(this, arguments);
        }
    });

    /**
     * This class provides a collection that can be used to list all webapps from a site. For
     * more information about a webapp see {@link BCAPI.Models.WebApp.App}.
     *
     * @name AppCollection
     * @class
     * @constructor
     * @memberOf BCAPI.Models.WebApp
     * @example
     * // fetch all available webapps
     * var appCollection = new BCAPI.Models.WebApp.AppCollection();
     *
     * appCollection.fetch({fetchFields: false,
     *  success: function(webapps) {
     *  	webapps.each(function(webapp) {
     *  		// no custom fields are retrieved.
     *  	});
     *  }
     *
     * @example
     * // extract and fetch webap details from a fetched collection (by webapp id).
     * var webappId = 1,
     *		webapp = appCollection.get(webappId);
     *
     * webapp.fetch({
     * 	success: function(webapp) {
     * 		// webapp is now fully loaded.
     *  }
     * });
     *
     * @example
     * // extract and fetch webapp details from fetched collection (by webapp index)
     * var idx = 1,
     * 	    webapp = appCollection.at(idx);
     *
     * webapp.fetch({
     * 	success: function(webapp) {
     * 		// webapp is now fully loaded.
     *  }
     * });
     */
    BCAPI.Models.WebApp.AppCollection = BCAPI.Models.Collection.extend({
        model: BCAPI.Models.WebApp.App,
    	/**
    	 * We override this method in order to transform each returned item into a strong typed
    	 * {@link BCAPI.Models.WebApp.App} models.
    	 *
    	 * @method
    	 * @instance
    	 * @param {Object} response The JSON response received from Web apps api.
    	 * @returns A list of web app items.
    	 * @memberOf BCAPI.Models.WebApp.AppCollection
         */
        parse: function(response) {
        	var webapps = [],
        		self = this;

        	response = BCAPI.Models.Collection.prototype.parse.call(this, response);

        	_.each(response, function(webapp) {
        		webapps.push(self.model(webapp));
        	});

        	return webapps;
        }
    });
})(jQuery);<commit_msg>added license header<commit_after>/*
*
* Copyright (c) 2012-2014 Adobe Systems Incorporated. All rights reserved.

* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
(function($) {
	"use strict";

	/**
	 * This namespace provides the models and collections for working with BC web applications and their items.
	 *
	 * @namespace BCAPI.Models.WebApp
	 * @memberOf BCAPI.Models
	 */
	BCAPI.Models.WebApp = {};

    /**
     * This class provides the model for interacting with web apps.
     *
     * ## Load webapps list
     *
     * ```javascript
     * var apps = new BCAPI.Models.WebApp.AppCollection();
     * apps.fetch({
     *		success: function(webAppItems) {
	 * 			// handle success
	 * 		},
	 * 		error: function(webAppItems, xhr) {
	 * 			// handle errors
	 * 		}
	 * });
     *
     * apps.each(function(webApp) {
	 * 		// display logic
	 * });
     * ```
     *
     * ## Create webapp
     *
     * ```javascript
     * var app = new BCAPI.Models.WebApp.App({
	 * 		"name": "Test app"
	 * });
     *
     * app.save({
	 * 		success: function(webAppItem) {
	 * 			// handle success
	 * 		},
	 * 		error: function(webAppItem, xhr) {
	 * 			// handle errors
	 * 		}
	 * });
     * ```
     *
     * If you want to refresh collections which rely on Item model please refresh those collections.
     *
     * ## Remove app
     *
     * ```javascript
     * var app = new BCAPI.Models.WebApp.App({name: "Test app"});
     * app.destroy({
	 * 	success: function(webAppItem, response) {
	 * 		// handle success here.
	 *  },
	 *  error: function(webAppItem, xhr, options) {
	 * 		// handle error scenario.
	 *  }
	 * });
     * ```
     *
     * ## Supported attributes
     *
     * ```javascript
     * var app = new BCAPI.Models.WebApp.App({
	 *	templateId: -1,
     *  uploadFolder: "images",
     *  requiresApproval: true,
     *  allowFileUpload: true,
     *  customerCanAdd: false,
     *  customerCanDelete: false,
     *  customerCanEdit: false,
     *  anyoneCanEdit: false,
     *  requiresPayment: false,
     *  validDays: -1, // never expire
     *  roleId: 0,
     *  hasAddress: false,
     *  disableDetailPages: false,
     *  locationEnabled: false
     * });
     * ```
     *
     * @class
     */
	BCAPI.Models.WebApp.App = BCAPI.Models.Model.extend({
        idAttribute: 'name',

        /**
         * Set tot true if you want to save or delete an existing item before fetching it
         *
         * @type {boolean}
         * @memberOf WebApp
         */
        isNotNew: null,

        isNew: function() {
            return this.isNotNew ? false : !this.get('id');
        },

        endpoint: function() {
            return '/api/v2/admin/sites/current/webapps';
        },

        fetch: function() {
            this.isNotNew = true;
            return Backbone.Model.prototype.fetch.apply(this, arguments);
        },

        destroy: function() {
            this.isNotNew = true;
            return Backbone.Model.prototype.destroy.apply(this, arguments);
        }
    });

    /**
     * This class provides a collection that can be used to list all webapps from a site. For
     * more information about a webapp see {@link BCAPI.Models.WebApp.App}.
     *
     * @name AppCollection
     * @class
     * @constructor
     * @memberOf BCAPI.Models.WebApp
     * @example
     * // fetch all available webapps
     * var appCollection = new BCAPI.Models.WebApp.AppCollection();
     *
     * appCollection.fetch({fetchFields: false,
     *  success: function(webapps) {
     *  	webapps.each(function(webapp) {
     *  		// no custom fields are retrieved.
     *  	});
     *  }
     *
     * @example
     * // extract and fetch webap details from a fetched collection (by webapp id).
     * var webappId = 1,
     *		webapp = appCollection.get(webappId);
     *
     * webapp.fetch({
     * 	success: function(webapp) {
     * 		// webapp is now fully loaded.
     *  }
     * });
     *
     * @example
     * // extract and fetch webapp details from fetched collection (by webapp index)
     * var idx = 1,
     * 	    webapp = appCollection.at(idx);
     *
     * webapp.fetch({
     * 	success: function(webapp) {
     * 		// webapp is now fully loaded.
     *  }
     * });
     */
    BCAPI.Models.WebApp.AppCollection = BCAPI.Models.Collection.extend({
        model: BCAPI.Models.WebApp.App,
    	/**
    	 * We override this method in order to transform each returned item into a strong typed
    	 * {@link BCAPI.Models.WebApp.App} models.
    	 *
    	 * @method
    	 * @instance
    	 * @param {Object} response The JSON response received from Web apps api.
    	 * @returns A list of web app items.
    	 * @memberOf BCAPI.Models.WebApp.AppCollection
         */
        parse: function(response) {
        	var webapps = [],
        		self = this;

        	response = BCAPI.Models.Collection.prototype.parse.call(this, response);

        	_.each(response, function(webapp) {
        		webapps.push(self.model(webapp));
        	});

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


<commit_before>/* WebRTC support for TowTruck.
   Note that this relies on parts of the interface located in chat.js and intro.js
   */
(function () {
  var TowTruck = window.TowTruck;
  var $ = TowTruck.$;
  var assert = TowTruck.assert;

  var PeerConnection = TowTruck.PeerConnection =
    window.mozRTCPeerConnection ||
    window.RTCPeerConnection ||
    window.webkitRTCPeerConnection;

  navigator.getUserMedia = navigator.getUserMedia ||
    navigator.mozGetUserMedia ||
    navigator.webkitGetUserMedia ||
    navigator.msGetUserMedia;

  TowTruck.RTCSupported = !! PeerConnection;

  TowTruck.on("ui-ready", function () {
    if (! TowTruck.RTCSupported) {
      $(".towtruck-rtc, [data-activate='towtruck-rtc']").hide();
      return;
    }
    $("#towtruck-mute").click(function () {
      var video = $("#towtruck-video")[0];
      video.muted = ! video.muted;
      if (video.muted) {
        $("#towtruck-mute").text("Unmute");
      } else {
        $("#towtruck-mute").text("Mute");
      }
    });
  });

  TowTruck.startPicPreview = function () {
    $("#towtruck-open-pic").show();
    $("#towtruck-open-pic").click(function () {
      $("#towtruck-open-pic").hide();
      $("#towtruck-pic-viewer").hide();
      $("#towtruck-accept-pic").hide();
      $("#towtruck-pic-container").show();
      if (! streaming) {
        startStreaming();
      }
    });

    function close() {
      $("#towtruck-open-pic").show();
      $("#towtruck-pic-container").hide();
    }
    $("#towtruck-pic-cancel").click(close);

    var video = $("#towtruck-pic-preview");
    var pic = $("#towtruck-pic-viewer");
    var streaming = false;
    assert(video.length);

    function startStreaming() {
      navigator.getUserMedia({
          video: true,
          audio: false
        },
        function(stream) {
          if (navigator.mozGetUserMedia) {
            video[0].mozSrcObject = stream;
          } else {
            var vendorURL = window.URL || window.webkitURL;
            video[0].src = vendorURL.createObjectURL(stream);
          }
          video[0].play();
        },
        function(err) {
          // FIXME: should pop up help or something in the case of a user
          // cancel
          console.error("getUserMedia error:", err);
        }
      );
    }

    video.on("canplay", function () {
      // This keeps us from asking for getUserMedia more than once:
      streaming = true;
    });

    $("#towtruck-take-pic").click(function () {
      var height = video[0].clientHeight;
      var width = video[0].clientWidth;
      var canvas = $("<canvas>");
      canvas.css({height: height + "px", width: width + "px"});
      var context = canvas[0].getContext("2d");
      context.drawImage(video[0], 0, 0, width, height);
      var data = canvas[0].toDataURL("image/png");
      // FIXME: for some reason this image is truncated on the bottom,
      // but I don't know why
      pic.attr("src", data).show();
      $("#towtruck-accept-pic").show();
    });

    $("#towtruck-accept-pic").click(function () {
      var imgData = pic.attr("src");
      TowTruck.settings("avatar", imgData);
      TowTruck.send({type: "nickname-update", avatar: imgData});
      close();
      updatePreview();
    });

    function updatePreview() {
      var avatar = TowTruck.settings("avatar");
      if (avatar) {
        $("#towtruck-avatar-view-container").show();
        $("#towtruck-avatar-view").attr("src", avatar);
      } else {
        $("#towtruck-avatar-view-container").hide();
      }
    }

    updatePreview();

  };

  TowTruck.chatSetup = false;

  TowTruck.peers.on("add update", function (peer) {
    TowTruck.setupChatInterface();
  });

  function addStream(video, stream) {
    video = $(video)[0];
    if (navigator.mozGetUserMedia) {
      video.mozSrcObject = stream;
    } else {
      var vendorURL = window.URL || window.webkitURL;
      video.src = vendorURL.createObjectURL(stream);
    }
  }

  TowTruck.setupChatInterface = function setupChatInterface() {
    if (! TowTruck.chat) {
      return;
    }
    var supported = false;
    TowTruck.peers.forEach(function (p) {
      if (p.rtcSupported) {
        supported = true;
      }
    });
    if (supported) {
      TowTruck.chat.find(".towtruck-start-video").show();
    } else {
      TowTruck.chat.find(".towtruck-start-video").hide();
      $("#towtruck-video").hide();
    }
    if (! setupChatInterface.bound) {
      $(".towtruck-start-video").click(TowTruck.startVideo);
      setupChatInterface.bound = true;
    }
  };

  TowTruck.rtc = {
    connection: null,
    offer: null,
    myOffer: null,
    answer: null,
    myAnswer: null,
    videoWanted: true
  };

  TowTruck.on("ui-showing-towtruck-rtc", function () {
    TowTruck.setupRTC();
  });

  TowTruck.messageHandler.on("rtc-offer", function (msg) {
    TowTruck.rtc.offer = msg.offer;
    TowTruck.setDescription(msg.offer, function () {
      TowTruck.activateTab("towtruck-rtc");
      TowTruck.setupRTC();
    });
  });

  TowTruck.messageHandler.on("rtc-answer", function (msg) {
    TowTruck.rtc.answer = msg.answer;
    TowTruck.setDescription(msg.answer, function () {
      TowTruck.setupRTC();
    });
  });

  TowTruck.startVideo = function startVideo() {
    TowTruck.rtc.videoWanted = true;
    TowTruck.setupRTC();
  };

  TowTruck.makeConnection = function (callback) {
    if (TowTruck.rtc.connection) {
      callback(TowTruck.rtc.connection);
      return;
    }
    var conn = PeerConnection();
    conn.onaddstream = function (event) {
      console.log("onaddstream", event);
      console.log("streams", conn.remoteStreams, conn.remoteStreams.length);
      var video = $("#towtruck-video");
      for (var i=0; i<conn.remoteStreams.length; i++) {
        var s = conn.remoteStreams[i];
        addStream(video, s);
      }
      video[0].play();
    };
    var video = $("#towtruck-video-me");
    assert(video.length);
    // FIXME: temporary hack for demo!
    var useVideo = ! TowTruck.isClient;
    navigator.mozGetUserMedia(
      {audio: true, video: useVideo},
      function (stream) {
        addStream(video, stream);
        video[0].play();
        TowTruck.rtc.localStream = stream;
        TowTruck.rtc.connection = conn;
        conn.addStream(stream);
        callback(conn);
      },
      function (error) {
        console.warn("Error in RTC getUserMedia:", error);
      }
    );
  };

  TowTruck.createAnswer = function (callback) {
    var conn = TowTruck.rtc.connection;
    assert(conn);
    assert(TowTruck.rtc.offer);
    conn.createAnswer(
      //TowTruck.rtc.offer,
      function (answer) {
        conn.setLocalDescription(
          answer,
          function () {
            callback(answer);
          },
          function (error) {
            console.warn("Error doing RTC setLocalDescription:", error);
          }
        );
      },
      function (error) {
        console.warn("Error doing RTC createAnswer:", error);
      }
    );
  };

  TowTruck.createOffer = function (callback) {
    var conn = TowTruck.rtc.connection;
    assert(conn);
    conn.createOffer(
      function (offer) {
        TowTruck.rtc.myOffer = offer;
        conn.setLocalDescription(
          offer,
          function () {
            callback(offer);
          },
          function (error) {
            console.warn("Error doing RTC setLocalDescription:", error);
          }
        );
      },
      function (error) {
        console.warn("Error doing RTC createOffer:", error);
      }
    );
  };

  TowTruck.setDescription = function (desc, callback) {
    TowTruck.makeConnection(function (conn) {
      conn.setRemoteDescription(
        desc,
        function () {
          if (callback) {
            callback();
          }
        },
        function (error) {
          console.warn("Error doing RTC setRemoteDescription:", error);
        }
      );
    });
  };

  TowTruck.setupRTC = function () {
    var rtc = TowTruck.rtc;
    if (! rtc.videoWanted) {
      if (rtc.offer) {
        TowTruck.addChat("Do you want to talk?  Press (v) to start video", "system");
      }
      return;
    }
    if (rtc.videoWanted && ! rtc.offer) {
      TowTruck.makeConnection(function () {
        TowTruck.createOffer(function (offer) {
          TowTruck.send({
            type: "rtc-offer",
            offer: offer
          });
        });
      });
      return;
    }
    if (rtc.videoWanted && rtc.offer && ! rtc.myAnswer) {
    console.log("sending answer");
      TowTruck.makeConnection(function () {
      console.log("connection created");
        TowTruck.createAnswer(function (answer) {
        console.log("answer created");
          TowTruck.send({
            type: "rtc-answer",
            answer: answer
          });
        });
      });
    }
  };

})();
<commit_msg>Some small/incomplete WebKit RTC fixes<commit_after>/* WebRTC support for TowTruck.
   Note that this relies on parts of the interface located in chat.js and intro.js
   */
(function () {
  var TowTruck = window.TowTruck;
  var $ = TowTruck.$;
  var assert = TowTruck.assert;

  var PeerConnection = TowTruck.PeerConnection =
    window.mozRTCPeerConnection ||
    window.webkitRTCPeerConnection ||
    window.RTCPeerConnection;

  navigator.getUserMedia = navigator.getUserMedia ||
    navigator.mozGetUserMedia ||
    navigator.webkitGetUserMedia ||
    navigator.msGetUserMedia;

  TowTruck.RTCSupported = !! PeerConnection;

  TowTruck.on("ui-ready", function () {
    if (! TowTruck.RTCSupported) {
      $(".towtruck-rtc, [data-activate='towtruck-rtc']").hide();
      return;
    }
    $("#towtruck-mute").click(function () {
      var video = $("#towtruck-video")[0];
      video.muted = ! video.muted;
      if (video.muted) {
        $("#towtruck-mute").text("Unmute");
      } else {
        $("#towtruck-mute").text("Mute");
      }
    });
  });

  TowTruck.startPicPreview = function () {
    $("#towtruck-open-pic").show();
    $("#towtruck-open-pic").click(function () {
      $("#towtruck-open-pic").hide();
      $("#towtruck-pic-viewer").hide();
      $("#towtruck-accept-pic").hide();
      $("#towtruck-pic-container").show();
      if (! streaming) {
        startStreaming();
      }
    });

    function close() {
      $("#towtruck-open-pic").show();
      $("#towtruck-pic-container").hide();
    }
    $("#towtruck-pic-cancel").click(close);

    var video = $("#towtruck-pic-preview");
    var pic = $("#towtruck-pic-viewer");
    var streaming = false;
    assert(video.length);

    function startStreaming() {
      navigator.getUserMedia({
          video: true,
          audio: false
        },
        function(stream) {
          if (navigator.mozGetUserMedia) {
            video[0].mozSrcObject = stream;
          } else {
            var vendorURL = window.URL || window.webkitURL;
            video[0].src = vendorURL.createObjectURL(stream);
          }
          video[0].play();
        },
        function(err) {
          // FIXME: should pop up help or something in the case of a user
          // cancel
          console.error("getUserMedia error:", err);
        }
      );
    }

    video.on("canplay", function () {
      // This keeps us from asking for getUserMedia more than once:
      streaming = true;
    });

    $("#towtruck-take-pic").click(function () {
      var height = video[0].clientHeight;
      var width = video[0].clientWidth;
      var canvas = $("<canvas>");
      canvas.css({height: height + "px", width: width + "px"});
      var context = canvas[0].getContext("2d");
      context.drawImage(video[0], 0, 0, width, height);
      var data = canvas[0].toDataURL("image/png");
      // FIXME: for some reason this image is truncated on the bottom,
      // but I don't know why
      pic.attr("src", data).show();
      $("#towtruck-accept-pic").show();
    });

    $("#towtruck-accept-pic").click(function () {
      var imgData = pic.attr("src");
      TowTruck.settings("avatar", imgData);
      TowTruck.send({type: "nickname-update", avatar: imgData});
      close();
      updatePreview();
    });

    function updatePreview() {
      var avatar = TowTruck.settings("avatar");
      if (avatar) {
        $("#towtruck-avatar-view-container").show();
        $("#towtruck-avatar-view").attr("src", avatar);
      } else {
        $("#towtruck-avatar-view-container").hide();
      }
    }

    updatePreview();

  };

  TowTruck.chatSetup = false;

  TowTruck.peers.on("add update", function (peer) {
    TowTruck.setupChatInterface();
  });

  function addStream(video, stream) {
    video = $(video)[0];
    if (navigator.mozGetUserMedia) {
      video.mozSrcObject = stream;
    } else {
      var vendorURL = window.URL || window.webkitURL;
      video.src = vendorURL.createObjectURL(stream);
    }
  }

  TowTruck.setupChatInterface = function setupChatInterface() {
    if (! TowTruck.chat) {
      return;
    }
    var supported = false;
    TowTruck.peers.forEach(function (p) {
      if (p.rtcSupported) {
        supported = true;
      }
    });
    if (supported) {
      TowTruck.chat.find(".towtruck-start-video").show();
    } else {
      TowTruck.chat.find(".towtruck-start-video").hide();
      $("#towtruck-video").hide();
    }
    if (! setupChatInterface.bound) {
      $(".towtruck-start-video").click(TowTruck.startVideo);
      setupChatInterface.bound = true;
    }
  };

  TowTruck.rtc = {
    connection: null,
    offer: null,
    myOffer: null,
    answer: null,
    myAnswer: null,
    videoWanted: true
  };

  TowTruck.on("ui-showing-towtruck-rtc", function () {
    TowTruck.setupRTC();
  });

  TowTruck.messageHandler.on("rtc-offer", function (msg) {
    TowTruck.rtc.offer = msg.offer;
    TowTruck.setDescription(msg.offer, function () {
      TowTruck.activateTab("towtruck-rtc");
      TowTruck.setupRTC();
    });
  });

  TowTruck.messageHandler.on("rtc-answer", function (msg) {
    TowTruck.rtc.answer = msg.answer;
    TowTruck.setDescription(msg.answer, function () {
      TowTruck.setupRTC();
    });
  });

  TowTruck.startVideo = function startVideo() {
    TowTruck.rtc.videoWanted = true;
    TowTruck.setupRTC();
  };

  TowTruck.makeConnection = function (callback) {
    if (TowTruck.rtc.connection) {
      callback(TowTruck.rtc.connection);
      return;
    }
    // FIXME: Chrome demands a configuration parameter here:
    var conn = new PeerConnection();
    conn.onaddstream = function (event) {
      console.log("onaddstream", event);
      console.log("streams", conn.remoteStreams, conn.remoteStreams.length);
      var video = $("#towtruck-video");
      for (var i=0; i<conn.remoteStreams.length; i++) {
        var s = conn.remoteStreams[i];
        addStream(video, s);
      }
      video[0].play();
    };
    var video = $("#towtruck-video-me");
    assert(video.length);
    // FIXME: temporary hack for demo!
    var useVideo = ! TowTruck.isClient;
    navigator.mozGetUserMedia(
      {audio: true, video: useVideo},
      function (stream) {
        addStream(video, stream);
        video[0].play();
        TowTruck.rtc.localStream = stream;
        TowTruck.rtc.connection = conn;
        conn.addStream(stream);
        callback(conn);
      },
      function (error) {
        console.warn("Error in RTC getUserMedia:", error);
      }
    );
  };

  TowTruck.createAnswer = function (callback) {
    var conn = TowTruck.rtc.connection;
    assert(conn);
    assert(TowTruck.rtc.offer);
    conn.createAnswer(
      //TowTruck.rtc.offer,
      function (answer) {
        conn.setLocalDescription(
          answer,
          function () {
            callback(answer);
          },
          function (error) {
            console.warn("Error doing RTC setLocalDescription:", error);
          }
        );
      },
      function (error) {
        console.warn("Error doing RTC createAnswer:", error);
      }
    );
  };

  TowTruck.createOffer = function (callback) {
    var conn = TowTruck.rtc.connection;
    assert(conn);
    conn.createOffer(
      function (offer) {
        TowTruck.rtc.myOffer = offer;
        conn.setLocalDescription(
          offer,
          function () {
            callback(offer);
          },
          function (error) {
            console.warn("Error doing RTC setLocalDescription:", error);
          }
        );
      },
      function (error) {
        console.warn("Error doing RTC createOffer:", error);
      }
    );
  };

  TowTruck.setDescription = function (desc, callback) {
    TowTruck.makeConnection(function (conn) {
      conn.setRemoteDescription(
        desc,
        function () {
          if (callback) {
            callback();
          }
        },
        function (error) {
          console.warn("Error doing RTC setRemoteDescription:", error);
        }
      );
    });
  };

  TowTruck.setupRTC = function () {
    var rtc = TowTruck.rtc;
    if (! rtc.videoWanted) {
      if (rtc.offer) {
        TowTruck.addChat("Do you want to talk?  Press (v) to start video", "system");
      }
      return;
    }
    if (rtc.videoWanted && ! rtc.offer) {
      TowTruck.makeConnection(function () {
        TowTruck.createOffer(function (offer) {
          TowTruck.send({
            type: "rtc-offer",
            offer: offer
          });
        });
      });
      return;
    }
    if (rtc.videoWanted && rtc.offer && ! rtc.myAnswer) {
    console.log("sending answer");
      TowTruck.makeConnection(function () {
      console.log("connection created");
        TowTruck.createAnswer(function (answer) {
        console.log("answer created");
          TowTruck.send({
            type: "rtc-answer",
            answer: answer
          });
        });
      });
    }
  };

})();
<|endoftext|>


<commit_before>(function() {

  function stateSetter(widget, state) {
    return function() {
      if(widget.view) {
        if(widget.rs.remote) {
          widget.view.setUserAddress(widget.rs.remote.userAddress);
        }
        widget.view.setState(state);
      }
    };
  }

  RemoteStorage.Widget = function(remoteStorage) {
    this.rs = remoteStorage;
    this.rs.on('ready', stateSetter(this, 'connected'));
    this.rs.on('disconnected', stateSetter(this, 'initial'));
    //this.rs.on('connecting', stateSetter(this, 'connecting'))
    this.rs.on('authing', stateSetter(this, 'authing'));
    this.rs.on('sync-busy', stateSetter(this, 'busy'));
    this.rs.on('sync-done', stateSetter(this, 'connected'));
  };

  RemoteStorage.Widget.prototype = {
    display: function() {
      if(! this.view) {
        this.setView(new View());
      }
      this.view.display.apply(this.view, arguments);
      return this;
    },

    setView: function(view) {
      this.view = view;
      this.view.on('connect', this.rs.connect.bind(this.rs));
      this.view.on('disconnect', this.rs.disconnect.bind(this.rs));
      this.view.on('sync', this.rs.sync.bind(this.rs));
    }
  };

  RemoteStorage.prototype.displayWidget = function() {
    this.widget.display();
  };

  RemoteStorage.Widget._rs_init = function(remoteStorage) {
    remoteStorage.widget = new RemoteStorage.Widget(remoteStorage);
    window.addEventListener('load', function() {
      remoteStorage.displayWidget();
    });
  };

  var cEl = document.createElement.bind(document);

  function gCl(parent, className) {
    return parent.getElementsByClassName(className)[0];
  }
  function gTl(parent, className) {
    return parent.getElementsByTagName(className)[0];
  }

  function toggle_bubble(widget) {
    var el = gCl(widget,'bubble');
    if(el.className.search('hidden') < 0) {
      addClass(el, 'hidden');
    } else {
      removeClass(el, 'hidden');
      gTl(widget, 'form').userAddress.focus();
    }
  }

  function show(el, display) {
    if(typeof(display) === 'undefined') {
      display = 'block';
    }
    el.style.display = display;
    return el;
  }

  function hide(el) {
    show(el,'none');
    return el;
  }

  // MAYBE do those two in a el.className.split(' ') way more safe
  function removeClass(el, className) {
    el.className = el.className.split(' ').filter(function(val) {
      return val != className;
    }).join(' ');
  }

  function addClass(el, className) {
    el.className += ' ' + className;
  }

  function View() {
    if(typeof(document) === 'undefined') {
      throw "Widget not supported";
    }
    RemoteStorage.eventHandling(this,
                                'connect',
                                'disconnect',
                                'sync',
                                'reconnect',
                                'display');

    this.display = function() {

      if(typeof(this.widget) !== 'undefined')
        return this.widget;

      var element = cEl('div');
      var style = cEl('style');
      style.innerHTML = RemoteStorage.Assets.widgetCss;

      element.id = "remotestorage-widget";

      element.innerHTML = RemoteStorage.Assets.widget;

      element.appendChild(style);
      document.body.appendChild(element);

      var el;
      //sync button
      el = gCl(element, 'sync');
      gTl(el, 'img').src = RemoteStorage.Assets.syncIcon;
      el.addEventListener('click', this.events.sync.bind(this));

      //disconnect button
      el = gCl(element, 'disconnect');
      gTl(el, 'img').src = RemoteStorage.Assets.disconnectIcon;
      el.addEventListener('click', this.events.disconnect.bind(this));

      //connect button
      var cb = gCl(element,'connect');
      gTl(cb, 'img').src = RemoteStorage.Assets.connectIcon;
      cb.addEventListener('click', this.events.connect.bind(this));

      // input
      el = gTl(element, 'form').userAddress;
      el.addEventListener('keyup', function(event) {
        if(event.target.value) cb.removeAttribute('disabled');
        else cb.setAttribute('disabled','disabled');
      });

      //the cube
      el = gCl(element, 'cube');
      el.src = RemoteStorage.Assets.remoteStorageIcon;
      el.addEventListener('click', function() {
        toggle_bubble(this.div);
      }.bind(this));

      this.div = element;

      this.states.initial.call(this);
      this._emit('display');
      return this.div;
    };

    this.setState = function(state) {
      var s = this.states[state];
      if(typeof(s) === 'undefined') {
        throw new Error("Bad State assigned to view: " + state);
      }
      s.call(this);
    };

    this.setUserAddress = function(addr) {
      this.userAddress = addr;
    };
  }

  View.prototype = {
    // States:
    //  initial      - not connected
    //  authing      - in auth flow
    //  connected    - connected to remote storage, not syncing at the moment
    //  busy         - connected, syncing at the moment
    //  offline      - connected, but no network connectivity
    //  error        - connected, but sync error happened
    //  unauthorized - connected, but request returned 401
    currentState : 'initial',
    states :  {
      initial : function() {
        this.div.className = "remotestorage-state-initial";
        gCl(this.div, 'status-text').innerHTML = "Connect remotestorage";
        var bubble = this.div.querySelector('.bubble');
        if(! bubble.classList.contains('hidden')) {
          bubble.classList.add('hidden');
        }
      },
      authing : function() {
        this.div.className = "remotestorage-state-authing";
        gCl(this.div, 'status-text').innerHTML = "Connecting";
        addClass(gCl(this.div, 'cube'), 'remotestorage-loading'); //TODO needs to be undone when is that neccesary
      },
      connected : function() {
        this.div.className = "remotestorage-state-connected";
        gCl(this.div, 'userAddress').innerHTML = this.userAddress;
        var cube = gCl(this.div, 'cube');
        cube.src = RemoteStorage.Assets.remoteStorageIcon;
        removeClass(cube, 'remotestorage-loading');
      },
      busy : function() {
        this.div.className = "remotestorage-state-busy";
        addClass(gCl(this.div, 'cube'), 'remotestorage-loading'); //TODO needs to be undone when is that neccesary
      },
      offline : function() {
        this.div.className = "remotestorage-state-offline";
        gCl(this.div, 'cube').src = RemoteStorage.Assets.remoteStorageIconOffline;
      },
      error : function() {
        this.div.className = "remotestorage-state-error";
        gCl(this.div, 'cube').src = RemoteStorage.Assets.remoteStorageIconError;
      },
      unauthorized : function() {
        this.div.className = "remotestorage-state-unauthorized";
      }
    },
    events : {
      connect : function(event) {
        event.preventDefault();
        this._emit('connect', gTl(this.div, 'form').userAddress.value);
      },
      sync : function(event) {
        event.preventDefault();
        this._emit('sync');
      },
      disconnect : function(event) {
        event.preventDefault();
        this._emit('disconnect');
      },
      // FIXME: what are these???
      reconnect : function() {},
      display : function() {}
    }
  };

})();
<commit_msg>remember last widget state in localStorage to reinitialize it correctly after a reload (fixes #347)<commit_after>(function(window) {

  var haveLocalStorage;
  var LS_STATE_KEY = "remotestorage:widget:state";

  function stateSetter(widget, state) {
    return function() {
      if(haveLocalStorage) {
        localStorage[LS_STATE_KEY] = state;
      }
      if(widget.view) {
        if(widget.rs.remote) {
          widget.view.setUserAddress(widget.rs.remote.userAddress);
        }
        widget.view.setState(state);
      }
    };
  }

  RemoteStorage.Widget = function(remoteStorage) {
    this.rs = remoteStorage;
    this.rs.on('ready', stateSetter(this, 'connected'));
    this.rs.on('disconnected', stateSetter(this, 'initial'));
    //this.rs.on('connecting', stateSetter(this, 'connecting'))
    this.rs.on('authing', stateSetter(this, 'authing'));
    this.rs.on('sync-busy', stateSetter(this, 'busy'));
    this.rs.on('sync-done', stateSetter(this, 'connected'));
    if(haveLocalStorage) {
      var state = localStorage[LS_STATE_KEY] = state;
      if(state) {
        this._rememberedState = state;
      }
    }
  };

  RemoteStorage.Widget.prototype = {
    display: function() {
      if(! this.view) {
        this.setView(new View());
      }
      this.view.display.apply(this.view, arguments);
      return this;
    },

    setView: function(view) {
      this.view = view;
      this.view.on('connect', this.rs.connect.bind(this.rs));
      this.view.on('disconnect', this.rs.disconnect.bind(this.rs));
      this.view.on('sync', this.rs.sync.bind(this.rs));
      if(this._rememberedState) {
        stateSetter(this, this._rememberedState)();
        delete this._rememberedState;
      }
    }
  };

  RemoteStorage.prototype.displayWidget = function() {
    this.widget.display();
  };

  RemoteStorage.Widget._rs_init = function(remoteStorage) {
    remoteStorage.widget = new RemoteStorage.Widget(remoteStorage);
    window.addEventListener('load', function() {
      remoteStorage.displayWidget();
    });
  };

  RemoteStorage.Widget._rs_supported = function(remoteStorage) {
    haveLocalStorage = 'localStorage' in window;
    return true;
  };

  var cEl = document.createElement.bind(document);

  function gCl(parent, className) {
    return parent.getElementsByClassName(className)[0];
  }
  function gTl(parent, className) {
    return parent.getElementsByTagName(className)[0];
  }

  function toggle_bubble(widget) {
    var el = gCl(widget,'bubble');
    if(el.className.search('hidden') < 0) {
      addClass(el, 'hidden');
    } else {
      removeClass(el, 'hidden');
      gTl(widget, 'form').userAddress.focus();
    }
  }

  function show(el, display) {
    if(typeof(display) === 'undefined') {
      display = 'block';
    }
    el.style.display = display;
    return el;
  }

  function hide(el) {
    show(el,'none');
    return el;
  }

  // MAYBE do those two in a el.className.split(' ') way more safe
  function removeClass(el, className) {
    el.className = el.className.split(' ').filter(function(val) {
      return val != className;
    }).join(' ');
  }

  function addClass(el, className) {
    el.className += ' ' + className;
  }

  function View() {
    if(typeof(document) === 'undefined') {
      throw "Widget not supported";
    }
    RemoteStorage.eventHandling(this,
                                'connect',
                                'disconnect',
                                'sync',
                                'reconnect',
                                'display');

    this.display = function() {

      if(typeof(this.widget) !== 'undefined')
        return this.widget;

      var element = cEl('div');
      var style = cEl('style');
      style.innerHTML = RemoteStorage.Assets.widgetCss;

      element.id = "remotestorage-widget";

      element.innerHTML = RemoteStorage.Assets.widget;

      element.appendChild(style);
      document.body.appendChild(element);

      var el;
      //sync button
      el = gCl(element, 'sync');
      gTl(el, 'img').src = RemoteStorage.Assets.syncIcon;
      el.addEventListener('click', this.events.sync.bind(this));

      //disconnect button
      el = gCl(element, 'disconnect');
      gTl(el, 'img').src = RemoteStorage.Assets.disconnectIcon;
      el.addEventListener('click', this.events.disconnect.bind(this));

      //connect button
      var cb = gCl(element,'connect');
      gTl(cb, 'img').src = RemoteStorage.Assets.connectIcon;
      cb.addEventListener('click', this.events.connect.bind(this));

      // input
      el = gTl(element, 'form').userAddress;
      el.addEventListener('keyup', function(event) {
        if(event.target.value) cb.removeAttribute('disabled');
        else cb.setAttribute('disabled','disabled');
      });

      //the cube
      el = gCl(element, 'cube');
      el.src = RemoteStorage.Assets.remoteStorageIcon;
      el.addEventListener('click', function() {
        toggle_bubble(this.div);
      }.bind(this));

      this.div = element;

      this.states.initial.call(this);
      this._emit('display');
      return this.div;
    };

    this.setState = function(state) {
      var s = this.states[state];
      if(typeof(s) === 'undefined') {
        throw new Error("Bad State assigned to view: " + state);
      }
      s.call(this);
    };

    this.setUserAddress = function(addr) {
      this.userAddress = addr;
    };
  }

  View.prototype = {
    // States:
    //  initial      - not connected
    //  authing      - in auth flow
    //  connected    - connected to remote storage, not syncing at the moment
    //  busy         - connected, syncing at the moment
    //  offline      - connected, but no network connectivity
    //  error        - connected, but sync error happened
    //  unauthorized - connected, but request returned 401
    currentState : 'initial',
    states :  {
      initial : function() {
        this.div.className = "remotestorage-state-initial";
        gCl(this.div, 'status-text').innerHTML = "Connect remotestorage";
        var bubble = this.div.querySelector('.bubble');
        if(! bubble.classList.contains('hidden')) {
          bubble.classList.add('hidden');
        }
      },
      authing : function() {
        this.div.className = "remotestorage-state-authing";
        gCl(this.div, 'status-text').innerHTML = "Connecting";
        addClass(gCl(this.div, 'cube'), 'remotestorage-loading'); //TODO needs to be undone when is that neccesary
      },
      connected : function() {
        this.div.className = "remotestorage-state-connected";
        gCl(this.div, 'userAddress').innerHTML = this.userAddress;
        var cube = gCl(this.div, 'cube');
        cube.src = RemoteStorage.Assets.remoteStorageIcon;
        removeClass(cube, 'remotestorage-loading');
      },
      busy : function() {
        this.div.className = "remotestorage-state-busy";
        addClass(gCl(this.div, 'cube'), 'remotestorage-loading'); //TODO needs to be undone when is that neccesary
      },
      offline : function() {
        this.div.className = "remotestorage-state-offline";
        gCl(this.div, 'cube').src = RemoteStorage.Assets.remoteStorageIconOffline;
      },
      error : function() {
        this.div.className = "remotestorage-state-error";
        gCl(this.div, 'cube').src = RemoteStorage.Assets.remoteStorageIconError;
      },
      unauthorized : function() {
        this.div.className = "remotestorage-state-unauthorized";
      }
    },
    events : {
      connect : function(event) {
        event.preventDefault();
        this._emit('connect', gTl(this.div, 'form').userAddress.value);
      },
      sync : function(event) {
        event.preventDefault();
        this._emit('sync');
      },
      disconnect : function(event) {
        event.preventDefault();
        this._emit('disconnect');
      },
      // FIXME: what are these???
      reconnect : function() {},
      display : function() {}
    }
  };

})(this);
<|endoftext|>


<commit_before>// Hook into the hot-reload to
// 1) wait until the application has resumed to reload the app
// 2) show the splashscreen while the app is reloading

var hasResumed = false;
var retryReloadFunc = null;

var retryReloadOnResume = function () {
  // Set resumed to true so _onMigrate performs the reload
  hasResumed = true;

  // Show the splashscreen during the reload
  // it will be hidden when the app starts
  navigator.splashscreen.show();

  // Re-run the onMigrate hooks
  retryReloadFunc();
};

Reload._onMigrate(function (retryReload) {
  retryReloadFunc = retryReload;

  // Prevent duplicate listeners in case _onMigrate is called multiple times
  document.removeEventListener("resume", retryReloadOnResume, false);

  if (!hasResumed) {
    document.addEventListener("resume", retryReloadOnResume, false);
  }

  // Reload the app if we have resumed
  return [hasResumed];
});
<commit_msg>Add check around splashscreen object<commit_after>// Hook into the hot-reload to
// 1) wait until the application has resumed to reload the app
// 2) show the splashscreen while the app is reloading

var hasResumed = false;
var retryReloadFunc = null;

var retryReloadOnResume = function () {
  // Set hasResumed to true so _onMigrate performs the reload
  hasResumed = true;

  // Show the splashscreen during the reload
  // it will hide when the app starts
  if (navigator.splashscreen) {
    navigator.splashscreen.show();
  }

  // Re-run the onMigrate hooks
  retryReloadFunc();
};

Reload._onMigrate(function (retry) {
  retryReloadFunc = retry;

  // Prevent duplicate listeners in case _onMigrate is called multiple times
  document.removeEventListener("resume", retryReloadOnResume, false);

  if (!hasResumed) {
    document.addEventListener("resume", retryReloadOnResume, false);
  }

  // Reload the app if we resumed
  return [hasResumed];
});
<|endoftext|>


<commit_before>const fs = require('fs');
const web3 = require('web3');
const utils = web3.utils;
const contractsDirectory = './src/contracts';
const tokensDirectory = './src/tokens';
const nftsDirectory = './src/nfts';

function renameTokens() {
  fs.readdirSync(tokensDirectory).forEach(folder => {
    fs.readdirSync(`${tokensDirectory}/${folder}`).forEach(file => {
      if (!utils.checkAddressChecksum(file.replace('.json', ''))) {
        fs.renameSync(
          `${tokensDirectory}/${folder}/${file}`,
          `${tokensDirectory}/${folder}/${utils
            .toChecksumAddress(file.replace('.json', ''))
            .toLowerCase()}.json`
        );
      }
    });
  });
}

function renameNfts() {
  fs.readdirSync(nftsDirectory).forEach(folder => {
    fs.readdirSync(`${nftsDirectory}/${folder}`).forEach(file => {
      if (!utils.checkAddressChecksum(file.replace('.json', ''))) {
        fs.renameSync(
          `${nftsDirectory}/${folder}/${file}`,
          `${nftsDirectory}/${folder}/${utils
            .toChecksumAddress(file.replace('.json', ''))
            .toLowerCase()}.json`
        );
      }
    });
  });
}

function renameContracts() {
  fs.readdirSync(contractsDirectory).forEach(folder => {
    fs.readdirSync(`${contractsDirectory}/${folder}`).forEach(file => {
      if (!utils.checkAddressChecksum(file.replace('.json', ''))) {
        fs.renameSync(
          `${contractsDirectory}/${folder}/${file}`,
          `${contractsDirectory}/${folder}/${utils
            .toChecksumAddress(file.replace('.json', ''))
            .toLowerCase()}.json`
        );
      }
    });
  });
}

function renameToChecksum() {
  renameTokens();
  renameContracts();
  renameNfts();
}

module.exports = renameToChecksum;
<commit_msg>Fix compile issue<commit_after>const fs = require('fs');
const web3 = require('web3');
const utils = web3.utils;
const contractsDirectory = './src/contracts';
const tokensDirectory = './src/tokens';
const nftsDirectory = './src/nfts';

function renameTokens() {
  fs.readdirSync(tokensDirectory).forEach(folder => {
    fs.readdirSync(`${tokensDirectory}/${folder}`).forEach(file => {
      if (!utils.checkAddressChecksum(file.replace('.json', ''))) {
        fs.renameSync(
          `${tokensDirectory}/${folder}/${file}`,
          `${tokensDirectory}/${folder}/${utils
            .toChecksumAddress(file.replace('.json', ''))}.json`
        );
      }
    });
  });
}

function renameNfts() {
  fs.readdirSync(nftsDirectory).forEach(folder => {
    fs.readdirSync(`${nftsDirectory}/${folder}`).forEach(file => {
      if (!utils.checkAddressChecksum(file.replace('.json', ''))) {
        fs.renameSync(
          `${nftsDirectory}/${folder}/${file}`,
          `${nftsDirectory}/${folder}/${utils
            .toChecksumAddress(file.replace('.json', ''))}.json`
        );
      }
    });
  });
}

function renameContracts() {
  fs.readdirSync(contractsDirectory).forEach(folder => {
    fs.readdirSync(`${contractsDirectory}/${folder}`).forEach(file => {
      if (!utils.checkAddressChecksum(file.replace('.json', ''))) {
        fs.renameSync(
          `${contractsDirectory}/${folder}/${file}`,
          `${contractsDirectory}/${folder}/${utils
            .toChecksumAddress(file.replace('.json', ''))}.json`
        );
      }
    });
  });
}

function renameToChecksum() {
  renameTokens();
  renameContracts();
  renameNfts();
}

module.exports = renameToChecksum;
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import './LoggedIn.css';
import AppBar from 'material-ui/AppBar';
import MenuItem from 'material-ui/MenuItem';
import IconButton from 'material-ui/IconButton';
import IconMenu from 'material-ui/IconMenu';
import { Link } from 'react-router-dom';
import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert';
import Paper from 'material-ui/Paper';
import PropTypes from 'prop-types';
import iOS from '../../images/ios.png'
import android from '../../images/android1.png'
import web from '../../images/network-icon.png'
import cms from '../../images/edit-icon-png-24.png'

const ListMenu = () => (
          <IconMenu className='IconMenu'
                      tooltip="Options"
                      iconButtonElement={
                          <IconButton
                          className='menu-icon'
                          iconStyle={{ fill : 'white',}}>
                              <MoreVertIcon /></IconButton>
                      }
                      targetOrigin={{ horizontal: 'right', vertical: 'top' }}
                      anchorOrigin={{ horizontal: 'right', vertical: 'top' }}
                     >
                     <MenuItem primaryText="Chat With Susi"
 										href="http://chat.susi.ai" />
                     <MenuItem primaryText="Change Password"
                           containerElement={<Link to="/changepassword" />} />
                           <MenuItem primaryText="delete account"
                           containerElement={<Link to="/delete-account" />} />
                    <MenuItem primaryText="Logout"
                    containerElement={<Link to="/logout" />} />
                  </IconMenu>

);
class LoggedIn extends Component {

  render (){

  const style = {
  height: 200,
  width: 200,
  marginTop: 100,
  marginLeft: 100,
  textAlign: 'center',
  display: 'inline-block',
  backgroundColor: '#f7f7f7',
};
const heading1 = {
  fontSize: 30,
  marginLeft: 160,
}
const heading2 = {
  fontSize: 30,
  marginLeft: 115,
}
const heading3 = {
  fontSize: 30,
  marginLeft: 148,
}
    return(

          <AppBar
            iconElementLeft={<iconButton ></iconButton>}
            className="app-bar"
            style={{ backgroundColor : '#4285F4',
               height: '46px'}}
            titleStyle={{height:'46px'}}
            iconElementRight={<ListMenu />}
          />

            <Link to="/settings">
              <Paper style={style} zDepth={1} circle={true}>

              </Paper>
            </Link>

iOS

            <Link to="/settings">
              <Paper style={style} zDepth={1} circle={true}>

              </Paper>
            </Link>

Android

            <Link to="/settings">
              <Paper style={style} zDepth={1} circle={true}>

              </Paper>
            </Link>

Web

            <Link to="/settings">
              <Paper style={style} zDepth={1} circle={true}>

              </Paper>
            </Link>

CMS

    )
  }
}

LoggedIn.propTypes = {
  history: PropTypes.object
};

export default LoggedIn;
<commit_msg>added link to browse skills (#152)<commit_after>import React, { Component } from 'react';
import './LoggedIn.css';
import AppBar from 'material-ui/AppBar';
import MenuItem from 'material-ui/MenuItem';
import IconButton from 'material-ui/IconButton';
import IconMenu from 'material-ui/IconMenu';
import { Link } from 'react-router-dom';
import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert';
import Paper from 'material-ui/Paper';
import PropTypes from 'prop-types';
import iOS from '../../images/ios.png'
import android from '../../images/android1.png'
import web from '../../images/network-icon.png'
import cms from '../../images/edit-icon-png-24.png'

const ListMenu = () => (
          <IconMenu className='IconMenu'
                      tooltip="Options"
                      iconButtonElement={
                          <IconButton
                          className='menu-icon'
                          iconStyle={{ fill : 'white',}}>
                              <MoreVertIcon /></IconButton>
                      }
                      targetOrigin={{ horizontal: 'right', vertical: 'top' }}
                      anchorOrigin={{ horizontal: 'right', vertical: 'top' }}
                     >
                     <MenuItem primaryText="Chat With Susi"
 										href="http://chat.susi.ai" />
 					<MenuItem primaryText="Browse Skills"
 										href="http://skills.susi.ai/" />
                     <MenuItem primaryText="Change Password"
                           containerElement={<Link to="/changepassword" />} />
                           <MenuItem primaryText="delete account"
                           containerElement={<Link to="/delete-account" />} />
                    <MenuItem primaryText="Logout"
                    containerElement={<Link to="/logout" />} />
                  </IconMenu>

);
class LoggedIn extends Component {

  render (){

  const style = {
  height: 200,
  width: 200,
  marginTop: 100,
  marginLeft: 100,
  textAlign: 'center',
  display: 'inline-block',
  backgroundColor: '#f7f7f7',
};
const heading1 = {
  fontSize: 30,
  marginLeft: 160,
}
const heading2 = {
  fontSize: 30,
  marginLeft: 115,
}
const heading3 = {
  fontSize: 30,
  marginLeft: 148,
}
    return(

          <AppBar
            iconElementLeft={<iconButton ></iconButton>}
            className="app-bar"
            style={{ backgroundColor : '#4285F4',
               height: '46px'}}
            titleStyle={{height:'46px'}}
            iconElementRight={<ListMenu />}
          />

            <Link to="/settings">
              <Paper style={style} zDepth={1} circle={true}>

              </Paper>
            </Link>

iOS

            <Link to="/settings">
              <Paper style={style} zDepth={1} circle={true}>

              </Paper>
            </Link>

Android

            <Link to="/settings">
              <Paper style={style} zDepth={1} circle={true}>

              </Paper>
            </Link>

Web

            <Link to="/settings">
              <Paper style={style} zDepth={1} circle={true}>

              </Paper>
            </Link>

CMS

    )
  }
}

LoggedIn.propTypes = {
  history: PropTypes.object
};

export default LoggedIn;
<|endoftext|>


<commit_before>var
    EchoRule  = require('../lib/rules/echo.js'),
    Heartbeat = require('../lib/rules/heartbeat.js'),
    LoadAverage = require('../lib/rules/load-average.js')
    ;

var rules = [];

rules.push(new EchoRule());
rules.push(new Heartbeat());
rules.push(new LoadAverage({
    warn: {
        1: 1.5,
        5: 0.9,
        15: 0.7
    },
    critical: {
        1: 1.5,
        5: 1.5,
        15: 1.5
    }
}));
rules.push(new Memory({
    warn: 0.7,
    critical: 0.9
});

module.exports = rules;
<commit_msg>Fix up the example ruleset<commit_after>var
    EchoRule  = require('../lib/rules/echo.js'),
    Heartbeat = require('../lib/rules/heartbeat.js'),
    LoadAverage = require('../lib/rules/load-average.js'),
    Memory = require('../lib/rules/memory.js')
    ;

var rules = [];

rules.push(new EchoRule());
rules.push(new Heartbeat());
rules.push(new LoadAverage({
    warn: {
        1: 1.5,
        5: 0.9,
        15: 0.7
    },
    critical: {
        1: 1.5,
        5: 1.5,
        15: 1.5
    }
}));
rules.push(new Memory({
    warn: 0.7,
    critical: 0.9
}));

module.exports = rules;
<|endoftext|>


<commit_before>import PropTypes from 'prop-types';
import React from 'react';
import { injectIntl, FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import { formValueSelector } from 'redux-form';
import { Grid, Row, Col } from 'react-flexbox-grid/lib';
import { push } from 'react-router-redux';
import * as d3 from 'd3';
import messages from '../../../resources/messages';
import QueryForm from './QueryForm';
import AppButton from '../../common/AppButton';
import ItemSlider from '../../common/ItemSlider';
import QueryPickerItem from './QueryPickerItem';
import { updateFeedback } from '../../../actions/appActions';
import QueryHelpDialog from '../../common/help/QueryHelpDialog';
import { selectQuery, updateQuery, addCustomQuery, loadUserSearches, saveUserSearch, deleteUserSearch, markAsDeletedQuery } from '../../../actions/explorerActions';
import { AddQueryButton } from '../../common/IconButton';
import { getPastTwoWeeksDateRange } from '../../../lib/dateUtil';
import { DEFAULT_COLLECTION_OBJECT_ARRAY, autoMagicQueryLabel, generateQueryParamString } from '../../../lib/explorerUtil';

const localMessages = {
  mainTitle: { id: 'explorer.querypicker.mainTitle', defaultMessage: 'Query List' },
  intro: { id: 'explorer.querypicker.intro', defaultMessage: 'Here are all available queries' },
  addQuery: { id: 'explorer.querypicker.addQuery', defaultMessage: 'Add query' },
  querySearch: { id: 'explorer.queryBuilder.advanced', defaultMessage: 'Search' },
  searchHint: { id: 'explorer.queryBuilder.hint', defaultMessage: 'Search' },
  deleteFailed: { id: 'explorer.queryBuilder.hint', defaultMessage: 'Sorry, deleting your search failed for some reason.' },
};

const formSelector = formValueSelector('queryForm');

class QueryPicker extends React.Component {
  // load selected if collections have been updated
 /* componentWillReceiveProps(nextProps) {
    const { queries, selected, handleQuerySelected } = nextProps;
    const selectedCollectionStatus = selected.collections.reduce((combined, c) => combined && c.tag_sets_id !== undefined, true);
    if (!selectedCollectionStatus) {
      const selectedIndex = queries.findIndex(q => q.id === selected.id);
      handleQuerySelected(queries[selectedIndex]);
    }
  } */
  addAQuery(newQueryObj) {
    const { addAQuery } = this.props;
    addAQuery(newQueryObj);
  }
  focusRequested = field => field.focus();

  updateDemoQueryLabel(query, newValue) {
    // update both label and q for query
    const { updateCurrentQuery } = this.props;
    const updatedQuery = { ...query };
    updatedQuery.label = newValue;
    updatedQuery.q = newValue;
    updatedQuery.autoNaming = false;
    updateCurrentQuery(updatedQuery, 'label');
  }
  // called by query picker to update things like label or color
  updateQueryProperty(query, propertyName, newValue) {
    const { updateCurrentQuery } = this.props;
    const updatedQuery = { ...query };
    updatedQuery[propertyName] = newValue;
    if (propertyName === 'label') { // no longer auto-name query if the user has intentionally changed it
      updatedQuery.autoNaming = false;
    }
    if (propertyName === 'q' && updatedQuery.autoNaming) { // no longer auto-name query if the user has intentionally changed it
      updatedQuery.label = newValue;
    }
    // now update it in the store
    updateCurrentQuery(updatedQuery, propertyName);
  }

  handleColorChange = (newColorInfo) => {
    // when user changes color we want to change it on all charts right away
    const { selected, updateCurrentQuery } = this.props;
    const updatedQuery = {
      ...selected,
      color: newColorInfo.value,
    };
    updateCurrentQuery(updatedQuery, 'color');
  }
  handleMediaDelete = (toBeDeletedObj) => {
    // the user has removed media from the Query Form SourceCollectionsFieldList
    const { selected, formQuery, updateCurrentQuery } = this.props; // formQuery same as selected
    // filter out removed ids...
    const updatedMedia = {
      ...selected,
      ...formQuery,
    };
    const updatedSources = formQuery.media.filter(m => m.id !== toBeDeletedObj.id && (m.type === 'source' || m.media_id));
    const updatedCollections = formQuery.media.filter(m => m.id !== toBeDeletedObj.id && (m.type === 'collection' || m.tags_id));
    updatedMedia.collections = updatedCollections;
    updatedMedia.sources = updatedSources;
    updateCurrentQuery(updatedMedia, null);
  }

  handleMediaChange = (sourceAndCollections) => {
    // the user has picked new sources and/or collections so we need to save in order to update the list onscreen
    const { selected, updateCurrentQueryThenReselect } = this.props;
    const updatedQuery = { ...selected };
    const updatedSources = sourceAndCollections.filter(m => m.type === 'source' || m.media_id);
    const updatedCollections = sourceAndCollections.filter(m => m.type === 'collection' || m.tags_id);
    updatedQuery.collections = updatedCollections;
    updatedQuery.sources = updatedSources;
    updateCurrentQueryThenReselect(updatedQuery);
  }

  isDeletable() {
    const { queries } = this.props;
    const unDeletedQueries = queries.filter(q => q.deleted !== true);
    return unDeletedQueries.length >= 2; // because we always have an empty query in the query array
  }

  handleDeleteAndSelectQuery(query) {
    const { queries, handleDeleteQuery } = this.props;
    const queryIndex = queries.findIndex(q => q.index !== null && q.index === query.index);
    const replaceSelectionWithWhichQuery = queryIndex === 0 ? 1 : 0; // replace with the query, not the position
    if (this.isDeletable()) {
      handleDeleteQuery(query, queries[replaceSelectionWithWhichQuery]);
    }
  }

  saveChangesToSelectedQuery() {
    const { selected, formQuery, updateCurrentQuery } = this.props;
    const updatedQuery = Object.assign({}, selected, formQuery);
    updateCurrentQuery(updatedQuery, 'label');
  }

  handleSelectedQueryChange(nextSelectedQuery, nextSelectedIndex) {
    const { handleQuerySelected } = this.props;
    // first update the one we are unmounting
    this.saveChangesToSelectedQuery();

    handleQuerySelected(nextSelectedQuery, nextSelectedQuery.index ? nextSelectedQuery.index : nextSelectedIndex);
  }

  saveAndSearch = () => {
    // wrap the save handler here because we need to save the changes to the selected query the user
    // might have made on the form, and then search
    const { onSearch, queries, isLoggedIn, updateOneQuery } = this.props;
    if (isLoggedIn) {
      this.saveChangesToSelectedQuery();
    } else {
      // for demo mode we have to save all the queries they entered first, and then search
      const nonDeletedQueries = queries.filter(q => q.deleted !== true);
      nonDeletedQueries.forEach((q) => {
        const queryText = document.getElementById(`query-${q.index}-q`).value;  // not super robust,
        const updatedQuery = {
          ...q,
          q: queryText,
        };
        updatedQuery.label = updatedQuery.autoNaming ? autoMagicQueryLabel(updatedQuery) : updatedQuery.label; // have to call this alone because input is the whole query
        updateOneQuery(updatedQuery);
      });
    }
    onSearch();
  }
  saveThisSearch = (queryName) => {
    const { queries, sendAndSaveUserSearch } = this.props; // formQuery same as selected
    // filter out removed ids...
    const searchstr = generateQueryParamString(queries.map(q => ({
      label: q.label,
      q: q.q,
      color: q.color,
      startDate: q.startDate,
      endDate: q.endDate,
      sources: q.sources,
      collections: q.collections,
    })));
    const userSearch = Object.assign({}, queryName, { timestamp: Date.now(), queryParams: searchstr });
    sendAndSaveUserSearch(userSearch);
  }

  render() {
    const { isLoggedIn, selected, queries, isEditable, handleLoadUserSearches, handleLoadSelectedSearch, handleDeleteUserSearch, savedSearches } = this.props;
    const { formatMessage } = this.props.intl;
    let queryPickerContent; // editable if demo mode
    let queryFormContent; // hidden if demo mode
    let fixedQuerySlides;
    let canSelectMedia = false;

    const unDeletedQueries = queries.filter(q => q.deleted !== true);
    if (unDeletedQueries && unDeletedQueries.length > 0 && selected) {
      fixedQuerySlides = unDeletedQueries.map((query, index) => (

          <QueryPickerItem
            isLoggedIn={isLoggedIn}
            key={index}
            query={query}
            isSelected={selected.index === query.index}
            isLabelEditable={isEditable} // if custom, true for either mode, else if logged in no
            isDeletable={() => this.isDeletable()}
            displayLabel={false}
            onQuerySelected={() => this.handleSelectedQueryChange(query, index)}
            updateQueryProperty={(propertyName, newValue) => this.updateQueryProperty(query, propertyName, newValue)}
            updateDemoQueryLabel={newValue => this.updateDemoQueryLabel(query, newValue)}
            onSearch={this.saveAndSearch}
            onDelete={() => this.handleDeleteAndSelectQuery(query)}
            // loadDialog={loadQueryEditDialog}
          />

      ));
      canSelectMedia = isLoggedIn;
      // provide the add Query button, load with default values when Added is clicked
      if (isLoggedIn || isEditable) {
        const colorPallette = idx => d3.schemeCategory10[idx % 10];
        const dateObj = getPastTwoWeeksDateRange();
        const newIndex = queries.length; // all queries, including 'deleted' ones
        const genDefColor = colorPallette(newIndex);
        const newQueryLabel = `Query ${String.fromCharCode('A'.charCodeAt(0) + newIndex)}`;
        const defaultQueryField = isLoggedIn ? '*' : '';
        const defaultQuery = { index: newIndex, label: newQueryLabel, q: defaultQueryField, description: 'new', startDate: dateObj.start, endDate: dateObj.end, collections: DEFAULT_COLLECTION_OBJECT_ARRAY, sources: [], color: genDefColor, autoNaming: true };

        const emptyQuerySlide = (

                <AddQueryButton
                  key={fixedQuerySlides.length} // this isn't working
                  tooltip={formatMessage(localMessages.addQuery)}
                  onClick={() => this.addAQuery(defaultQuery)}
                />
                 this.addAQuery(defaultQuery)}><FormattedMessage {...localMessages.addQuery} />

        );

        fixedQuerySlides.push(emptyQuerySlide);
      }
      let demoSearchButtonContent; // in demo mode we need a search button outside the form (cause we can't see the form)
      if (!isLoggedIn) {
        demoSearchButtonContent = (
          <Grid>
            <Row>
              <Col lg={10}>

                  <QueryHelpDialog />

              </Col>
              <Col lg={1}>
                <AppButton
                  style={{ marginTop: 30 }}
                  type="submit"
                  label={formatMessage(messages.search)}
                  primary
                  onTouchTap={this.saveAndSearch}
                />
              </Col>
            </Row>
          </Grid>
        );
      }
      queryPickerContent = (

            <Grid>
              <ItemSlider
                title={formatMessage(localMessages.intro)}
                slides={fixedQuerySlides}
                settings={{ height: 60, dots: false, slidesToShow: 4, slidesToScroll: 1, infinite: false, arrows: fixedQuerySlides.length > 4 }}
              />
            </Grid>

          {demoSearchButtonContent}

      );
      if (isLoggedIn) {  // if logged in show full form
        queryFormContent = (
          <QueryForm
            initialValues={selected}
            selected={selected}
            searchNickname={queries.map(q => q.label).join(', ')}
            savedSearches={savedSearches}
            form="queryForm"
            enableReinitialize
            destroyOnUnmount={false}
            buttonLabel={formatMessage(localMessages.querySearch)}
            onSave={this.saveAndSearch}
            onColorChange={this.handleColorChange}
            onMediaChange={this.handleMediaChange}
            onMediaDelete={this.handleMediaDelete}
            onDateChange={(dateObject, newValue) => this.updateQueryProperty(selected, dateObject.currentTarget.name, newValue)}
            handleLoadSearches={handleLoadUserSearches}
            handleLoadSelectedSearch={handleLoadSelectedSearch}
            handleSaveSearch={l => this.saveThisSearch(l)}
            handleDeleteSearch={l => handleDeleteUserSearch(l)}
            isEditable={canSelectMedia}
            focusRequested={this.focusRequested}
          />
        );
      }
      // indicate which queryPickerItem is selected -
      // const selectedWithSandCLabels = queries.find(q => q.index === selected.index);
      return (

          {queryPickerContent}
          {queryFormContent}

      );
    }
    return (

error - no queries

    );
  }
}

QueryPicker.propTypes = {
  // from state
  selected: PropTypes.object,
  queries: PropTypes.array,
  isLoggedIn: PropTypes.bool.isRequired,
  formQuery: PropTypes.object,
  // from composition
  intl: PropTypes.object.isRequired,
  // from dispatch
  handleQuerySelected: PropTypes.func.isRequired,
  updateCurrentQuery: PropTypes.func.isRequired,
  updateCurrentQueryThenReselect: PropTypes.func.isRequired,
  addAQuery: PropTypes.func.isRequired,
  handleLoadUserSearches: PropTypes.func.isRequired,
  handleLoadSelectedSearch: PropTypes.func.isRequired,
  savedSearches: PropTypes.array.isRequired,
  sendAndSaveUserSearch: PropTypes.func.isRequired,
  handleDeleteUserSearch: PropTypes.func.isRequired,
  handleDeleteQuery: PropTypes.func.isRequired,
  updateOneQuery: PropTypes.func.isRequired,
  // from parent
  isEditable: PropTypes.bool.isRequired,
  isDeletable: PropTypes.func,
  onSearch: PropTypes.func.isRequired,
};

const mapStateToProps = state => ({
  selected: state.explorer.selected,
  queries: state.explorer.queries.queries ? state.explorer.queries.queries : null,
  isLoggedIn: state.user.isLoggedIn,
  formQuery: formSelector(state, 'q', 'color', 'media', 'startDate', 'endDate'),
  savedSearches: state.explorer.savedSearches.list,
});

const mapDispatchToProps = (dispatch, ownProps) => ({
  handleQuerySelected: (query, index) => {
    const queryWithIndex = Object.assign({}, query, { index }); // if this doesn't exist...
    dispatch(selectQuery(queryWithIndex));
  },
  updateCurrentQueryThenReselect: (query, fieldName) => {
    if (query) {
      dispatch(updateQuery({ query, fieldName }));
      dispatch(selectQuery(query));
    }
  },
  updateCurrentQuery: (query, fieldName) => {
    if (query) {
      dispatch(updateQuery({ query, fieldName }));
    }
  },
  updateOneQuery: (query) => {
    dispatch(updateQuery({ query }));
  },
  addAQuery: (query) => {
    if (query) {
      dispatch(addCustomQuery(query));
      dispatch(selectQuery(query));
    }
  },
  handleLoadUserSearches: () => {
    dispatch(loadUserSearches());
  },
  handleLoadSelectedSearch: (selectedSearch) => {
    if (selectedSearch && selectedSearch.queryParams) {
      // this does not cause the url to reload
      dispatch(push({ pathname: '/queries/search', search: `?q=${selectedSearch.queryParams}` }));
      // but this does
      // window.location.href = `#/queries/search?q=${selectedSearch.queryParams}`;
    }
  },
  handleDeleteUserSearch: (selectedSearch) => {
    if (selectedSearch && selectedSearch.queryName) {
      dispatch(deleteUserSearch(selectedSearch))
      .then((results) => {
        if (results.success) {
          dispatch(loadUserSearches());
        } else {
          dispatch(updateFeedback({
            open: true,
            message: ownProps.intl.formatMessage(localMessages.deleteFailed),
          }));
        }
      });
    }
  },
  sendAndSaveUserSearch: (savedSearch) => {
    if (savedSearch) {
      dispatch(saveUserSearch(savedSearch));
    }
  },
  handleDeleteQuery: (query, replacementSelectionQuery) => {
    if (query) {
      dispatch(markAsDeletedQuery(query));
      dispatch(selectQuery(replacementSelectionQuery));
    }
  },
});

export default
  injectIntl(
    connect(mapStateToProps, mapDispatchToProps)(
      QueryPicker
    )
  );

<commit_msg>#820 set window.location to load saved search<commit_after>import PropTypes from 'prop-types';
import React from 'react';
import { injectIntl, FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import { formValueSelector } from 'redux-form';
import { Grid, Row, Col } from 'react-flexbox-grid/lib';
// import { push } from 'react-router-redux';
import * as d3 from 'd3';
import messages from '../../../resources/messages';
import QueryForm from './QueryForm';
import AppButton from '../../common/AppButton';
import ItemSlider from '../../common/ItemSlider';
import QueryPickerItem from './QueryPickerItem';
import { updateFeedback } from '../../../actions/appActions';
import QueryHelpDialog from '../../common/help/QueryHelpDialog';
import { selectQuery, updateQuery, addCustomQuery, loadUserSearches, saveUserSearch, deleteUserSearch, markAsDeletedQuery } from '../../../actions/explorerActions';
import { AddQueryButton } from '../../common/IconButton';
import { getPastTwoWeeksDateRange } from '../../../lib/dateUtil';
import { DEFAULT_COLLECTION_OBJECT_ARRAY, autoMagicQueryLabel, generateQueryParamString } from '../../../lib/explorerUtil';

const localMessages = {
  mainTitle: { id: 'explorer.querypicker.mainTitle', defaultMessage: 'Query List' },
  intro: { id: 'explorer.querypicker.intro', defaultMessage: 'Here are all available queries' },
  addQuery: { id: 'explorer.querypicker.addQuery', defaultMessage: 'Add query' },
  querySearch: { id: 'explorer.queryBuilder.advanced', defaultMessage: 'Search' },
  searchHint: { id: 'explorer.queryBuilder.hint', defaultMessage: 'Search' },
  deleteFailed: { id: 'explorer.queryBuilder.hint', defaultMessage: 'Sorry, deleting your search failed for some reason.' },
};

const formSelector = formValueSelector('queryForm');

class QueryPicker extends React.Component {
  // load selected if collections have been updated
 /* componentWillReceiveProps(nextProps) {
    const { queries, selected, handleQuerySelected } = nextProps;
    const selectedCollectionStatus = selected.collections.reduce((combined, c) => combined && c.tag_sets_id !== undefined, true);
    if (!selectedCollectionStatus) {
      const selectedIndex = queries.findIndex(q => q.id === selected.id);
      handleQuerySelected(queries[selectedIndex]);
    }
  } */
  addAQuery(newQueryObj) {
    const { addAQuery } = this.props;
    addAQuery(newQueryObj);
  }
  focusRequested = field => field.focus();

  updateDemoQueryLabel(query, newValue) {
    // update both label and q for query
    const { updateCurrentQuery } = this.props;
    const updatedQuery = { ...query };
    updatedQuery.label = newValue;
    updatedQuery.q = newValue;
    updatedQuery.autoNaming = false;
    updateCurrentQuery(updatedQuery, 'label');
  }
  // called by query picker to update things like label or color
  updateQueryProperty(query, propertyName, newValue) {
    const { updateCurrentQuery } = this.props;
    const updatedQuery = { ...query };
    updatedQuery[propertyName] = newValue;
    if (propertyName === 'label') { // no longer auto-name query if the user has intentionally changed it
      updatedQuery.autoNaming = false;
    }
    if (propertyName === 'q' && updatedQuery.autoNaming) { // no longer auto-name query if the user has intentionally changed it
      updatedQuery.label = newValue;
    }
    // now update it in the store
    updateCurrentQuery(updatedQuery, propertyName);
  }

  handleColorChange = (newColorInfo) => {
    // when user changes color we want to change it on all charts right away
    const { selected, updateCurrentQuery } = this.props;
    const updatedQuery = {
      ...selected,
      color: newColorInfo.value,
    };
    updateCurrentQuery(updatedQuery, 'color');
  }
  handleMediaDelete = (toBeDeletedObj) => {
    // the user has removed media from the Query Form SourceCollectionsFieldList
    const { selected, formQuery, updateCurrentQuery } = this.props; // formQuery same as selected
    // filter out removed ids...
    const updatedMedia = {
      ...selected,
      ...formQuery,
    };
    const updatedSources = formQuery.media.filter(m => m.id !== toBeDeletedObj.id && (m.type === 'source' || m.media_id));
    const updatedCollections = formQuery.media.filter(m => m.id !== toBeDeletedObj.id && (m.type === 'collection' || m.tags_id));
    updatedMedia.collections = updatedCollections;
    updatedMedia.sources = updatedSources;
    updateCurrentQuery(updatedMedia, null);
  }

  handleMediaChange = (sourceAndCollections) => {
    // the user has picked new sources and/or collections so we need to save in order to update the list onscreen
    const { selected, updateCurrentQueryThenReselect } = this.props;
    const updatedQuery = { ...selected };
    const updatedSources = sourceAndCollections.filter(m => m.type === 'source' || m.media_id);
    const updatedCollections = sourceAndCollections.filter(m => m.type === 'collection' || m.tags_id);
    updatedQuery.collections = updatedCollections;
    updatedQuery.sources = updatedSources;
    updateCurrentQueryThenReselect(updatedQuery);
  }

  isDeletable() {
    const { queries } = this.props;
    const unDeletedQueries = queries.filter(q => q.deleted !== true);
    return unDeletedQueries.length >= 2; // because we always have an empty query in the query array
  }

  handleDeleteAndSelectQuery(query) {
    const { queries, handleDeleteQuery } = this.props;
    const queryIndex = queries.findIndex(q => q.index !== null && q.index === query.index);
    const replaceSelectionWithWhichQuery = queryIndex === 0 ? 1 : 0; // replace with the query, not the position
    if (this.isDeletable()) {
      handleDeleteQuery(query, queries[replaceSelectionWithWhichQuery]);
    }
  }

  saveChangesToSelectedQuery() {
    const { selected, formQuery, updateCurrentQuery } = this.props;
    const updatedQuery = Object.assign({}, selected, formQuery);
    updateCurrentQuery(updatedQuery, 'label');
  }

  handleSelectedQueryChange(nextSelectedQuery, nextSelectedIndex) {
    const { handleQuerySelected } = this.props;
    // first update the one we are unmounting
    this.saveChangesToSelectedQuery();

    handleQuerySelected(nextSelectedQuery, nextSelectedQuery.index ? nextSelectedQuery.index : nextSelectedIndex);
  }

  saveAndSearch = () => {
    // wrap the save handler here because we need to save the changes to the selected query the user
    // might have made on the form, and then search
    const { onSearch, queries, isLoggedIn, updateOneQuery } = this.props;
    if (isLoggedIn) {
      this.saveChangesToSelectedQuery();
    } else {
      // for demo mode we have to save all the queries they entered first, and then search
      const nonDeletedQueries = queries.filter(q => q.deleted !== true);
      nonDeletedQueries.forEach((q) => {
        const queryText = document.getElementById(`query-${q.index}-q`).value;  // not super robust,
        const updatedQuery = {
          ...q,
          q: queryText,
        };
        updatedQuery.label = updatedQuery.autoNaming ? autoMagicQueryLabel(updatedQuery) : updatedQuery.label; // have to call this alone because input is the whole query
        updateOneQuery(updatedQuery);
      });
    }
    onSearch();
  }
  saveThisSearch = (queryName) => {
    const { queries, sendAndSaveUserSearch } = this.props; // formQuery same as selected
    // filter out removed ids...
    const searchstr = generateQueryParamString(queries.map(q => ({
      label: q.label,
      q: q.q,
      color: q.color,
      startDate: q.startDate,
      endDate: q.endDate,
      sources: q.sources,
      collections: q.collections,
    })));
    const userSearch = Object.assign({}, queryName, { timestamp: Date.now(), queryParams: searchstr });
    sendAndSaveUserSearch(userSearch);
  }

  render() {
    const { isLoggedIn, selected, queries, isEditable, handleLoadUserSearches, handleLoadSelectedSearch, handleDeleteUserSearch, savedSearches } = this.props;
    const { formatMessage } = this.props.intl;
    let queryPickerContent; // editable if demo mode
    let queryFormContent; // hidden if demo mode
    let fixedQuerySlides;
    let canSelectMedia = false;

    const unDeletedQueries = queries.filter(q => q.deleted !== true);
    if (unDeletedQueries && unDeletedQueries.length > 0 && selected) {
      fixedQuerySlides = unDeletedQueries.map((query, index) => (

          <QueryPickerItem
            isLoggedIn={isLoggedIn}
            key={index}
            query={query}
            isSelected={selected.index === query.index}
            isLabelEditable={isEditable} // if custom, true for either mode, else if logged in no
            isDeletable={() => this.isDeletable()}
            displayLabel={false}
            onQuerySelected={() => this.handleSelectedQueryChange(query, index)}
            updateQueryProperty={(propertyName, newValue) => this.updateQueryProperty(query, propertyName, newValue)}
            updateDemoQueryLabel={newValue => this.updateDemoQueryLabel(query, newValue)}
            onSearch={this.saveAndSearch}
            onDelete={() => this.handleDeleteAndSelectQuery(query)}
            // loadDialog={loadQueryEditDialog}
          />

      ));
      canSelectMedia = isLoggedIn;
      // provide the add Query button, load with default values when Added is clicked
      if (isLoggedIn || isEditable) {
        const colorPallette = idx => d3.schemeCategory10[idx % 10];
        const dateObj = getPastTwoWeeksDateRange();
        const newIndex = queries.length; // all queries, including 'deleted' ones
        const genDefColor = colorPallette(newIndex);
        const newQueryLabel = `Query ${String.fromCharCode('A'.charCodeAt(0) + newIndex)}`;
        const defaultQueryField = isLoggedIn ? '*' : '';
        const defaultQuery = { index: newIndex, label: newQueryLabel, q: defaultQueryField, description: 'new', startDate: dateObj.start, endDate: dateObj.end, collections: DEFAULT_COLLECTION_OBJECT_ARRAY, sources: [], color: genDefColor, autoNaming: true };

        const emptyQuerySlide = (

                <AddQueryButton
                  key={fixedQuerySlides.length} // this isn't working
                  tooltip={formatMessage(localMessages.addQuery)}
                  onClick={() => this.addAQuery(defaultQuery)}
                />
                 this.addAQuery(defaultQuery)}><FormattedMessage {...localMessages.addQuery} />

        );

        fixedQuerySlides.push(emptyQuerySlide);
      }
      let demoSearchButtonContent; // in demo mode we need a search button outside the form (cause we can't see the form)
      if (!isLoggedIn) {
        demoSearchButtonContent = (
          <Grid>
            <Row>
              <Col lg={10}>

                  <QueryHelpDialog />

              </Col>
              <Col lg={1}>
                <AppButton
                  style={{ marginTop: 30 }}
                  type="submit"
                  label={formatMessage(messages.search)}
                  primary
                  onTouchTap={this.saveAndSearch}
                />
              </Col>
            </Row>
          </Grid>
        );
      }
      queryPickerContent = (

            <Grid>
              <ItemSlider
                title={formatMessage(localMessages.intro)}
                slides={fixedQuerySlides}
                settings={{ height: 60, dots: false, slidesToShow: 4, slidesToScroll: 1, infinite: false, arrows: fixedQuerySlides.length > 4 }}
              />
            </Grid>

          {demoSearchButtonContent}

      );
      if (isLoggedIn) {  // if logged in show full form
        queryFormContent = (
          <QueryForm
            initialValues={selected}
            selected={selected}
            searchNickname={queries.map(q => q.label).join(', ')}
            savedSearches={savedSearches}
            form="queryForm"
            enableReinitialize
            destroyOnUnmount={false}
            buttonLabel={formatMessage(localMessages.querySearch)}
            onSave={this.saveAndSearch}
            onColorChange={this.handleColorChange}
            onMediaChange={this.handleMediaChange}
            onMediaDelete={this.handleMediaDelete}
            onDateChange={(dateObject, newValue) => this.updateQueryProperty(selected, dateObject.currentTarget.name, newValue)}
            handleLoadSearches={handleLoadUserSearches}
            handleLoadSelectedSearch={handleLoadSelectedSearch}
            handleSaveSearch={l => this.saveThisSearch(l)}
            handleDeleteSearch={l => handleDeleteUserSearch(l)}
            isEditable={canSelectMedia}
            focusRequested={this.focusRequested}
          />
        );
      }
      // indicate which queryPickerItem is selected -
      // const selectedWithSandCLabels = queries.find(q => q.index === selected.index);
      return (

          {queryPickerContent}
          {queryFormContent}

      );
    }
    return (

error - no queries

    );
  }
}

QueryPicker.propTypes = {
  // from state
  selected: PropTypes.object,
  queries: PropTypes.array,
  isLoggedIn: PropTypes.bool.isRequired,
  formQuery: PropTypes.object,
  // from composition
  intl: PropTypes.object.isRequired,
  // from dispatch
  handleQuerySelected: PropTypes.func.isRequired,
  updateCurrentQuery: PropTypes.func.isRequired,
  updateCurrentQueryThenReselect: PropTypes.func.isRequired,
  addAQuery: PropTypes.func.isRequired,
  handleLoadUserSearches: PropTypes.func.isRequired,
  handleLoadSelectedSearch: PropTypes.func.isRequired,
  savedSearches: PropTypes.array.isRequired,
  sendAndSaveUserSearch: PropTypes.func.isRequired,
  handleDeleteUserSearch: PropTypes.func.isRequired,
  handleDeleteQuery: PropTypes.func.isRequired,
  updateOneQuery: PropTypes.func.isRequired,
  // from parent
  isEditable: PropTypes.bool.isRequired,
  isDeletable: PropTypes.func,
  onSearch: PropTypes.func.isRequired,
};

const mapStateToProps = state => ({
  selected: state.explorer.selected,
  queries: state.explorer.queries.queries ? state.explorer.queries.queries : null,
  isLoggedIn: state.user.isLoggedIn,
  formQuery: formSelector(state, 'q', 'color', 'media', 'startDate', 'endDate'),
  savedSearches: state.explorer.savedSearches.list,
});

const mapDispatchToProps = (dispatch, ownProps) => ({
  handleQuerySelected: (query, index) => {
    const queryWithIndex = Object.assign({}, query, { index }); // if this doesn't exist...
    dispatch(selectQuery(queryWithIndex));
  },
  updateCurrentQueryThenReselect: (query, fieldName) => {
    if (query) {
      dispatch(updateQuery({ query, fieldName }));
      dispatch(selectQuery(query));
    }
  },
  updateCurrentQuery: (query, fieldName) => {
    if (query) {
      dispatch(updateQuery({ query, fieldName }));
    }
  },
  updateOneQuery: (query) => {
    dispatch(updateQuery({ query }));
  },
  addAQuery: (query) => {
    if (query) {
      dispatch(addCustomQuery(query));
      dispatch(selectQuery(query));
    }
  },
  handleLoadUserSearches: () => {
    dispatch(loadUserSearches());
  },
  handleLoadSelectedSearch: (selectedSearch) => {
    if (selectedSearch && selectedSearch.queryParams) {
      window.location = `https://explorer.mediacloud.org/#/queries/search?q=${selectedSearch.queryParams}`;
    }
  },
  handleDeleteUserSearch: (selectedSearch) => {
    if (selectedSearch && selectedSearch.queryName) {
      dispatch(deleteUserSearch(selectedSearch))
      .then((results) => {
        if (results.success) {
          dispatch(loadUserSearches());
        } else {
          dispatch(updateFeedback({
            open: true,
            message: ownProps.intl.formatMessage(localMessages.deleteFailed),
          }));
        }
      });
    }
  },
  sendAndSaveUserSearch: (savedSearch) => {
    if (savedSearch) {
      dispatch(saveUserSearch(savedSearch));
    }
  },
  handleDeleteQuery: (query, replacementSelectionQuery) => {
    if (query) {
      dispatch(markAsDeletedQuery(query));
      dispatch(selectQuery(replacementSelectionQuery));
    }
  },
});

export default
  injectIntl(
    connect(mapStateToProps, mapDispatchToProps)(
      QueryPicker
    )
  );

<|endoftext|>


<commit_before>import React from 'react';
import styled from 'styled-components';

import CourseList from 'atoms/course-list';

const Language = styled.li``;

const LanguageEducation = ({ education }) => {
  return (
    <CourseList>
      {education.map(lang => (
        <Language key={lang.id}>{lang.label}</Language>
      ))}
    </CourseList>
  );
};

export default LanguageEducation;
<commit_msg>Increase padding for language-education<commit_after>import React from 'react';
import styled from 'styled-components';

import CourseList from 'atoms/course-list';

const StyledCourseList = styled(CourseList)`
  padding: 1rem 0;
`;

const Language = styled.li``;

const LanguageEducation = ({ education }) => {
  return (
    <StyledCourseList>
      {education.map(lang => (
        <Language key={lang.id}>{lang.label}</Language>
      ))}
    </StyledCourseList>
  );
};

export default LanguageEducation;
<|endoftext|>


<commit_before>require('string.prototype.startswith');
require('string.prototype.endswith');

var fs = require('fs');

require('babel-register')({
  plugins: ['transform-es2015-modules-commonjs']
});

var L20n = require('../../src/runtime/node');
var Context = require('../../src/lib/context').Context;

var ftlCode = fs.readFileSync(__dirname + '/example.ftl').toString();

var data = {
  "brandShortName": "BRANDSHORTNAME",
  "ssid": "SSID",
  "capabilities": "CAPABILITIES",
  "linkSpeed": "LINKSPEED",
  "pin": "PIN",
  "n": 3,
  "name": "NAME",
  "device": "DEVICE",
  "code": "CODE",
  "app": "APP",
  "size": 100,
  "unit": "UNIT",
  "list": "LIST",
  "level": "LEVEL",
  "number": "NUMBER",
  "link1": "LINK1",
  "link2": "LINK2",
  "count": 10,
};

var lang = {
  code:'en-US',
  src: 'app',
};
function micro(time) {
  // time is [seconds, nanoseconds]
  return Math.round((time[0] * 1e9 + time[1]) / 1000);
}

var cumulative = {};
var start = process.hrtime();

cumulative.ftlParseStart = process.hrtime(start);
var ast = L20n.FTLASTParser.parseResource(ftlCode);
cumulative.ftlParseEnd = process.hrtime(start);

cumulative.ftlEntriesParseStart = process.hrtime(start);
var entries = L20n.FTLEntriesParser.parseResource(ftlCode);
cumulative.ftlEntriesParseEnd = process.hrtime(start);

var entries = L20n.createEntriesFromAST(ast).entries;
var ctx = new L20n.MockContext(entries);

cumulative.format = process.hrtime(start);
for (var id in entries) {
  L20n.format(ctx, lang, data, entries[id]);
}
cumulative.formatEnd = process.hrtime(start);

var results = {
  ftlParse: micro(cumulative.ftlParseEnd) - micro(cumulative.ftlParseStart),
  ftlEntriesParse: micro(cumulative.ftlEntriesParseEnd) - micro(cumulative.ftlEntriesParseStart),
  format: micro(cumulative.formatEnd) - micro(cumulative.format),
};
console.log(JSON.stringify(results));
<commit_msg>rename result columns in perf/benchmark.node.js<commit_after>require('string.prototype.startswith');
require('string.prototype.endswith');

var fs = require('fs');

require('babel-register')({
  plugins: ['transform-es2015-modules-commonjs']
});

var L20n = require('../../src/runtime/node');
var Context = require('../../src/lib/context').Context;

var ftlCode = fs.readFileSync(__dirname + '/example.ftl').toString();

var data = {
  "brandShortName": "BRANDSHORTNAME",
  "ssid": "SSID",
  "capabilities": "CAPABILITIES",
  "linkSpeed": "LINKSPEED",
  "pin": "PIN",
  "n": 3,
  "name": "NAME",
  "device": "DEVICE",
  "code": "CODE",
  "app": "APP",
  "size": 100,
  "unit": "UNIT",
  "list": "LIST",
  "level": "LEVEL",
  "number": "NUMBER",
  "link1": "LINK1",
  "link2": "LINK2",
  "count": 10,
};

var lang = {
  code:'en-US',
  src: 'app',
};
function micro(time) {
  // time is [seconds, nanoseconds]
  return Math.round((time[0] * 1e9 + time[1]) / 1000);
}

var cumulative = {};
var start = process.hrtime();

cumulative.ftlParseStart = process.hrtime(start);
var ast = L20n.FTLASTParser.parseResource(ftlCode);
cumulative.ftlParseEnd = process.hrtime(start);

cumulative.ftlEntriesParseStart = process.hrtime(start);
var entries = L20n.FTLEntriesParser.parseResource(ftlCode);
cumulative.ftlEntriesParseEnd = process.hrtime(start);

var entries = L20n.createEntriesFromAST(ast).entries;
var ctx = new L20n.MockContext(entries);

cumulative.format = process.hrtime(start);
for (var id in entries) {
  L20n.format(ctx, lang, data, entries[id]);
}
cumulative.formatEnd = process.hrtime(start);

var results = {
  parseFTL: micro(cumulative.ftlParseEnd) - micro(cumulative.ftlParseStart),
  parseFTLEntries: micro(cumulative.ftlEntriesParseEnd) - micro(cumulative.ftlEntriesParseStart),
  format: micro(cumulative.formatEnd) - micro(cumulative.format),
};
console.log(JSON.stringify(results));
<|endoftext|>