<commit_before>/*
 * jQuery One Page Nav Plugin
 * http://github.com/davist11/jQuery-One-Page-Nav
 *
 * Copyright (c) 2010 Trevor Davis (http://trevordavis.net)
 * Dual licensed under the MIT and GPL licenses.
 * Uses the same license as jQuery, see:
 * http://jquery.org/license
 *
 * @version 2.0
 *
 * Example usage:
 * $('#nav').onePageNav({
 *   currentClass: 'current',
 *   changeHash: false,
 *   scrollSpeed: 750
 * });
 */

;(function($, window, document, undefined){

	// our plugin constructor
	var OnePageNav = function(elem, options){
		this.elem = elem;
		this.$elem = $(elem);
		this.options = options;
		this.metadata = this.$elem.data('plugin-options');
		this.$nav = this.$elem.find('a');
		this.$win = $(window);
		this.sections = {};
		this.didScroll = false;
		this.$doc = $(document);
		this.docHeight = this.$doc.height();
	};

	// the plugin prototype
	OnePageNav.prototype = {
		defaults: {
			currentClass: 'current',
			changeHash: false,
			easing: 'swing',
			filter: '',
			scrollSpeed: 750,
			scrollOffset: 0,
			scrollThreshold: 0.5,
			begin: false,
			end: false
		},

		init: function() {
			var self = this;

			// Introduce defaults that can be extended either
			// globally or using an object literal.
			self.config = $.extend({}, self.defaults, self.options, self.metadata);

			//Filter any links out of the nav
			if(self.config.filter !== '') {
				self.$nav = self.$nav.filter(self.config.filter);
			}

			//Handle clicks on the nav
			self.$nav.on('click.onePageNav', $.proxy(self.handleClick, self));

			//Get the section positions
			self.getPositions();

			//Handle scroll changes
			self.bindInterval();

			//Update the positions on resize too
			self.$win.on('resize.onePageNav', $.proxy(self.getPositions, self));

			return this;
		},

		adjustNav: function(self, $parent) {
			self.$elem.find('.' + self.config.currentClass).removeClass(self.config.currentClass);
			$parent.addClass(self.config.currentClass);
		},

		bindInterval: function() {
			var self = this;
			var docHeight;

			self.$win.on('scroll.onePageNav', function() {
				self.didScroll = true;
			});

			self.t = setInterval(function() {
				docHeight = self.$doc.height();

				//If it was scrolled
				if(self.didScroll) {
					self.didScroll = false;
					self.scrollChange();
				}

				//If the document height changes
				if(docHeight !== self.docHeight) {
					self.docHeight = docHeight;
					self.getPositions();
				}
			}, 250 );
		},

		getHash: function($link) {
			return $link.attr('href').split("#")[1];
		},

		getPositions: function() {
			var self = this;
			var linkHref;
			var topPos;

			self.$nav.each(function() {
				linkHref = self.getHash($(this));
				topPos = $("#" + linkHref).offset().top;

				self.sections[linkHref] = Math.round(topPos) - self.config.scrollOffset;
			});
		},

		getSection: function(windowPos) {
			var returnValue = null;
			var windowHeight = Math.round(this.$win.height() * this.config.scrollThreshold);

			for(var section in this.sections) {
				if((this.sections[section] - windowHeight) < windowPos) {
					returnValue = section;
				}
			}

			return returnValue;
		},

		handleClick: function(e) {
			var self = this;
			var $link = $(e.currentTarget);
			var $parent = $link.parent();
			var newLoc = '#' + self.getHash($link);

			if(!$parent.hasClass(self.config.currentClass)) {
				//Start callback
				if(self.config.begin) {
					self.config.begin();
				}

				//Change the highlighted nav item
				self.adjustNav(self, $parent);

				//Removing the auto-adjust on scroll
				self.unbindInterval();

				//Scroll to the correct position
				$.scrollTo(newLoc, self.config.scrollSpeed, {
					axis: 'y',
					easing: self.config.easing,
					offset: {
						top: -self.config.scrollOffset
					},
					onAfter: function() {
						//Do we need to change the hash?
						if(self.config.changeHash) {
							window.location.hash = newLoc;
						}

						//Add the auto-adjust on scroll back in
						self.bindInterval();

						//End callback
						if(self.config.end) {
							self.config.end();
						}
            window.location.hash = newLoc;
					}
				});
			}

			e.preventDefault();
		},

		scrollChange: function() {
			var windowTop = this.$win.scrollTop();
			var position = this.getSection(windowTop);
			var $parent;

			//If the position is set
			if(position !== null) {
				$parent = this.$elem.find('a[href$="#' + position + '"]').parent();

				//Change the highlighted nav item
				this.adjustNav(this, $parent);
			}
		},

		unbindInterval: function() {
			clearInterval(this.t);
			this.$win.unbind('scroll.onePageNav');
		}
	};

	OnePageNav.defaults = OnePageNav.prototype.defaults;

	$.fn.onePageNav = function(options) {
		return this.each(function() {
			new OnePageNav(this, options).init();
		});
	};

})( jQuery, window , document );
<commit_msg>update to jquery nav plugin<commit_after>/*
 * jQuery One Page Nav Plugin
 * http://github.com/davist11/jQuery-One-Page-Nav
 *
 * Copyright (c) 2010 Trevor Davis (http://trevordavis.net)
 * Dual licensed under the MIT and GPL licenses.
 * Uses the same license as jQuery, see:
 * http://jquery.org/license
 *
 * @version 2.0
 *
 * Example usage:
 * $('#nav').onePageNav({
 *   currentClass: 'current',
 *   changeHash: false,
 *   scrollSpeed: 750
 * });
 */

;(function($, window, document, undefined){

	// our plugin constructor
	var OnePageNav = function(elem, options){
		this.elem = elem;
		this.$elem = $(elem);
		this.options = options;
		this.metadata = this.$elem.data('plugin-options');
		this.$nav = this.$elem.find('a');
		this.$win = $(window);
		this.sections = {};
		this.didScroll = false;
		this.$doc = $(document);
		this.docHeight = this.$doc.height();
	};

	// the plugin prototype
	OnePageNav.prototype = {
		defaults: {
			currentClass: 'current',
			changeHash: false,
			easing: 'swing',
			filter: '',
			scrollSpeed: 750,
			scrollOffset: 0,
			scrollThreshold: 0.5,
			begin: false,
			end: false
		},

		init: function() {
			var self = this;

			// Introduce defaults that can be extended either
			// globally or using an object literal.
			self.config = $.extend({}, self.defaults, self.options, self.metadata);

			//Filter any links out of the nav
			if(self.config.filter !== '') {
				self.$nav = self.$nav.filter(self.config.filter);
			}

			//Handle clicks on the nav
			self.$nav.on('click.onePageNav', $.proxy(self.handleClick, self));

			//Get the section positions
			self.getPositions();

			//Handle scroll changes
			self.bindInterval();

			//Update the positions on resize too
			self.$win.on('resize.onePageNav', $.proxy(self.getPositions, self));

			return this;
		},

		adjustNav: function(self, $parent) {
			self.$elem.find('.' + self.config.currentClass).removeClass(self.config.currentClass);
			$parent.addClass(self.config.currentClass);
		},

		bindInterval: function() {
			var self = this;
			var docHeight;

			self.$win.on('scroll.onePageNav', function() {
				self.didScroll = true;
			});

			self.t = setInterval(function() {
				docHeight = self.$doc.height();

				//If it was scrolled
				if(self.didScroll) {
					self.didScroll = false;
					self.scrollChange();
				}

				//If the document height changes
				if(docHeight !== self.docHeight) {
					self.docHeight = docHeight;
					self.getPositions();
				}
			}, 250 );
		},

		getHash: function($link) {
			return $link.attr('href').split("#")[1];
		},

		getPositions: function() {
			var self = this;
			var linkHref;
			var topPos;

			self.$nav.each(function() {
				linkHref = self.getHash($(this));
				topPos = $("#" + linkHref).offset().top;

				self.sections[linkHref] = Math.round(topPos) - self.config.scrollOffset;
			});
		},

		getSection: function(windowPos) {
			var returnValue = null;
			var windowHeight = Math.round(this.$win.height() * this.config.scrollThreshold);

			for(var section in this.sections) {
				if((this.sections[section] - windowHeight) < windowPos) {
					returnValue = section;
				}
			}

			return returnValue;
		},

		handleClick: function(e) {
			var self = this;
			var $link = $(e.currentTarget);
			var $parent = $link.parent();
			var newLoc = '#' + self.getHash($link);

			if(!$parent.hasClass(self.config.currentClass)) {
				//Start callback
				if(self.config.begin) {
					self.config.begin();
				}

				//Change the highlighted nav item
				self.adjustNav(self, $parent);

				//Removing the auto-adjust on scroll
				self.unbindInterval();

				//Scroll to the correct position
				$.scrollTo(newLoc, self.config.scrollSpeed, {
					axis: 'y',
					easing: self.config.easing,
					offset: {
						top: -self.config.scrollOffset
					},
					onAfter: function() {
						//Do we need to change the hash?
						if(self.config.changeHash) {
							window.location.hash = newLoc;
						}

						//Add the auto-adjust on scroll back in
						self.bindInterval();

						//End callback
						if(self.config.end) {
							self.config.end();
						}
					}
				});
			}

			e.preventDefault();
		},

		scrollChange: function() {
			var windowTop = this.$win.scrollTop();
			var position = this.getSection(windowTop);
			var $parent;

			//If the position is set
			if(position !== null) {
				$parent = this.$elem.find('a[href$="#' + position + '"]').parent();

				//Change the highlighted nav item
				this.adjustNav(this, $parent);
			}
		},

		unbindInterval: function() {
			clearInterval(this.t);
			this.$win.unbind('scroll.onePageNav');
		}
	};

	OnePageNav.defaults = OnePageNav.prototype.defaults;

	$.fn.onePageNav = function(options) {
		return this.each(function() {
			new OnePageNav(this, options).init();
		});
	};

})( jQuery, window , document );
<|endoftext|>


<commit_before>async function findHotel(timestamp) {
  document.getElementById('hotel-results').innerHTML = LOADING_ANIMATION_HTML;
  if (numLocations !== getNumPlaceObjectsInArray(locationPlaceObjects)) {
    M.Toast.dismissAll();
    M.toast({
      html: 'Not all of your places are selected through autocomplete.',
    });
    return;
  }
  const elem = document.getElementById('hotel-modal');
  const instance = M.Modal.getInstance(elem);
  instance.open();

  // Get center point from which to start searching for hotels
  const coords = placesToCoordsWeightArray(locationPlaceObjects);
  const [lat, lng] = centerOfMass(coords);
  document.getElementById('hotels-map').innerHTML = '';
  document.getElementById('hotels-map').style.display = 'none';

  const response = await fetch(
    `https://cors-anywhere.herokuapp.com/https://maps.googleapis.com/maps/api/place/textsearch/json?type=lodging&location=${lat},${lng}&radius=10000&key=${GOOGLE_API_KEY}&output=json`
  );
  const { results } = await response.json();
  parseAndRenderHotelResults(results, { lat: lat, lng: lng }, timestamp);
}

async function parseAndRenderHotelResults(json, centerPoint, timestamp) {
  const modalContent = document.getElementById('hotel-results');
  const hotelsMapElem = document.getElementById('hotels-map');
  hotelsMapElem.style.height = '';

  if (!json || json.length === 0) {
    modalContent.innerText =
      "We couldn't find any hotels nearby. Sorry about that.";
  } else {
    json = json.slice(0, 10);
    const hotelMap = new google.maps.Map(
      document.getElementById('hotels-map'),
      {
        center: centerPoint,
        zoom: 12,
      }
    );
    // Add existing locations to the map
    markers.forEach((obj) => {
      const { position } = obj;
      const { lat, lng } = position;
      const marker = new google.maps.Marker({
        position: { lat: lat(), lng: lng() },
        map: hotelMap,
        title: obj.title,
      });
    });
    // Add distance_center and photo_url fields to each object in
    // json.results
    json = json.map(async (obj) => {
      const { geometry } = obj;
      const { location } = geometry;
      const marker = new google.maps.Marker({
        position: location,
        map: hotelMap,
        title: obj.name,
        label: {
          fontFamily: 'Material Icons',
          text: 'hotel',
        },
      });
      const infoWindow = new google.maps.InfoWindow({
        content: `<h5 class="infowindow-text">${obj.name}</h5>`,
      });
      marker.addListener('click', () => infoWindow.open(hotelMap, marker));
      obj.distance_center = distanceBetween(location, centerPoint);
      const photoRef =
        obj.photos && Array.isArray(obj.photos)
          ? obj.photos[0].photo_reference
          : undefined;
      if (photoRef) {
        const photoUrl = await imageURLFromPhotoRef(photoRef);
        obj.photo_url = photoUrl;
        obj.photo_ref = photoRef;
      } else {
        obj.photo_url = undefined;
        obj.photo_ref = '';
      }
      return await obj;
    });
    json = await Promise.all(json);
    json.sort((a, b) => a.distance_center - b.distance_center);

    hotelsMapElem.style.display = 'block';
    hotelsMapElem.style.width = '100%';
    hotelsMapElem.style.height = '400px';
    hotelsMapElem.style.marginBottom = '2em';
    modalContent.innerHTML = json
      .map(
        ({ name, formatted_address, rating, place_id, photo_url, photo_ref }) =>
          `

` +
          (photo_url
            ? `

 `
            : '') +
          `

                  ${name}

${formatted_address}

                  <button
                    class="btn indigo"
                    onClick="saveTrip('${place_id}',
                                      '${photo_ref}',
                                      '${name.replace(/'/g, "\\'")}',
                                       ${timestamp})"
                  >
                    CHOOSE
                  </button>

                  infoInfo

Rating: ${rating}

More coming soon!

        `
      )
      .join('');
  }
}

<commit_msg>Add util functions to open and close hotel modal<commit_after>//const DOM elements for components of shared hotel modal
const HOTEL_MAP = document.getElementById('hotel-map');
const HOTEL_RESULTS = document.getElementById('hotel-results');

async function findHotel(timestamp) {
  document.getElementById('hotel-results').innerHTML = LOADING_ANIMATION_HTML;
  if (numLocations !== getNumPlaceObjectsInArray(locationPlaceObjects)) {
    M.Toast.dismissAll();
    M.toast({
      html: 'Not all of your places are selected through autocomplete.',
    });
    return;
  }
  const elem = document.getElementById('hotel-modal');
  const instance = M.Modal.getInstance(elem);
  instance.open();

  // Get center point from which to start searching for hotels
  const coords = placesToCoordsWeightArray(locationPlaceObjects);
  const [lat, lng] = centerOfMass(coords);
  document.getElementById('hotels-map').innerHTML = '';
  document.getElementById('hotels-map').style.display = 'none';

  const response = await fetch(
    `https://cors-anywhere.herokuapp.com/https://maps.googleapis.com/maps/api/place/textsearch/json?type=lodging&location=${lat},${lng}&radius=10000&key=${GOOGLE_API_KEY}&output=json`
  );
  const { results } = await response.json();
  parseAndRenderHotelResults(results, { lat: lat, lng: lng }, timestamp);
}

async function parseAndRenderHotelResults(json, centerPoint, timestamp) {
  const modalContent = document.getElementById('hotel-results');
  const hotelsMapElem = document.getElementById('hotels-map');
  hotelsMapElem.style.height = '';

  if (!json || json.length === 0) {
    modalContent.innerText =
      "We couldn't find any hotels nearby. Sorry about that.";
  } else {
    json = json.slice(0, 10);
    const hotelMap = new google.maps.Map(
      document.getElementById('hotels-map'),
      {
        center: centerPoint,
        zoom: 12,
      }
    );
    // Add existing locations to the map
    markers.forEach((obj) => {
      const { position } = obj;
      const { lat, lng } = position;
      const marker = new google.maps.Marker({
        position: { lat: lat(), lng: lng() },
        map: hotelMap,
        title: obj.title,
      });
    });
    // Add distance_center and photo_url fields to each object in
    // json.results
    json = json.map(async (obj) => {
      const { geometry } = obj;
      const { location } = geometry;
      const marker = new google.maps.Marker({
        position: location,
        map: hotelMap,
        title: obj.name,
        label: {
          fontFamily: 'Material Icons',
          text: 'hotel',
        },
      });
      const infoWindow = new google.maps.InfoWindow({
        content: `<h5 class="infowindow-text">${obj.name}</h5>`,
      });
      marker.addListener('click', () => infoWindow.open(hotelMap, marker));
      obj.distance_center = distanceBetween(location, centerPoint);
      const photoRef =
        obj.photos && Array.isArray(obj.photos)
          ? obj.photos[0].photo_reference
          : undefined;
      if (photoRef) {
        const photoUrl = await imageURLFromPhotoRef(photoRef);
        obj.photo_url = photoUrl;
        obj.photo_ref = photoRef;
      } else {
        obj.photo_url = undefined;
        obj.photo_ref = '';
      }
      return await obj;
    });
    json = await Promise.all(json);
    json.sort((a, b) => a.distance_center - b.distance_center);

    hotelsMapElem.style.display = 'block';
    hotelsMapElem.style.width = '100%';
    hotelsMapElem.style.height = '400px';
    hotelsMapElem.style.marginBottom = '2em';
    modalContent.innerHTML = json
      .map(
        ({ name, formatted_address, rating, place_id, photo_url, photo_ref }) =>
          `

` +
          (photo_url
            ? `

 `
            : '') +
          `

                  ${name}

${formatted_address}

                  <button
                    class="btn indigo"
                    onClick="saveTrip('${place_id}',
                                      '${photo_ref}',
                                      '${name.replace(/'/g, "\\'")}',
                                       ${timestamp})"
                  >
                    CHOOSE
                  </button>

                  infoInfo

Rating: ${rating}

More coming soon!

        `
      )
      .join('');
  }
}

function openHotelModal() {
  const HOTEL_MODAL_ELEM = document.getElementById('hotel-modal');
  const HOTEL_MODAL_INSTANCE = M.Modal.getInstance(HOTEL_MODAL_ELEM);
  HOTEL_MODAL_INSTANCE.open();
}

function closeHotelModal() {
  const HOTEL_MODAL_ELEM = document.getElementById('hotel-modal');
  const HOTEL_MODAL_INSTANCE = M.Modal.getInstance(HOTEL_MODAL_ELEM);
  HOTEL_MODAL_INSTANCE.close();
}

<|endoftext|>


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

angular.module('zeppelinWebApp').controller('NavCtrl', NavCtrl);

NavCtrl.$inject = [
  '$scope',
  '$rootScope',
  '$http',
  '$routeParams',
  '$location',
  'noteListDataFactory',
  'baseUrlSrv',
  'websocketMsgSrv',
  'arrayOrderingSrv',
  'searchService',
  'TRASH_FOLDER_ID'
];

function NavCtrl($scope, $rootScope, $http, $routeParams, $location,
                 noteListDataFactory, baseUrlSrv, websocketMsgSrv,
                 arrayOrderingSrv, searchService, TRASH_FOLDER_ID) {
  var vm = this;
  vm.arrayOrderingSrv = arrayOrderingSrv;
  vm.connected = websocketMsgSrv.isConnected();
  vm.isActive = isActive;
  vm.logout = logout;
  vm.notes = noteListDataFactory;
  vm.search = search;
  vm.searchForm = searchService;
  vm.showLoginWindow = showLoginWindow;
  vm.TRASH_FOLDER_ID = TRASH_FOLDER_ID;
  vm.isFilterNote = isFilterNote;

  $scope.query = {q: ''};

  initController();

  function getZeppelinVersion() {
    $http.get(baseUrlSrv.getRestApiBase() + '/version').success(
      function(data, status, headers, config) {
        $rootScope.zeppelinVersion = data.body;
      }).error(
      function(data, status, headers, config) {
        console.log('Error %o %o', status, data.message);
      });
  }

  function initController() {
    $scope.isDrawNavbarNoteList = false;
    angular.element('#notebook-list').perfectScrollbar({suppressScrollX: true});

    angular.element(document).click(function() {
      $scope.query.q = '';
    });

    getZeppelinVersion();
    loadNotes();
  }

  function isFilterNote(note) {
    if (!$scope.query.q) {
      return true;
    }

    var noteName = note.name;
    if (noteName.toLowerCase().indexOf($scope.query.q.toLowerCase()) > -1) {
      return true;
    }
    return false;
  }

  function isActive(noteId) {
    return ($routeParams.noteId === noteId);
  }

  function listConfigurations() {
    websocketMsgSrv.listConfigurations();
  }

  function loadNotes() {
    websocketMsgSrv.getNoteList();
  }

  function logout() {
    var logoutURL = baseUrlSrv.getRestApiBase() + '/login/logout';

    //for firefox and safari
    logoutURL = logoutURL.replace('//', '//false:false@');
    $http.post(logoutURL).error(function() {
      //force authcBasic (if configured) to logout
      $http.post(logoutURL).error(function() {
        $rootScope.userName = '';
        $rootScope.ticket.principal = '';
        $rootScope.ticket.ticket = '';
        $rootScope.ticket.roles = '';
        BootstrapDialog.show({
          message: 'Logout Success'
        });
        setTimeout(function() {
          window.location.replace('/');
        }, 1000);
      });
    });
  }

  function search(searchTerm) {
    $location.path('/search/' + searchTerm);
  }

  function showLoginWindow() {
    setTimeout(function() {
      angular.element('#userName').focus();
    }, 500);
  }

  /*
   ** $scope.$on functions below
   */

  $scope.$on('setNoteMenu', function(event, notes) {
    noteListDataFactory.setNotes(notes);
    initNotebookListEventListener();
  });

  $scope.$on('setConnectedStatus', function(event, param) {
    vm.connected = param;
  });

  $scope.$on('loginSuccess', function(event, param) {
    listConfigurations();
    loadNotes();
  });

  /*
   ** Performance optimization for Browser Render.
   */
  function initNotebookListEventListener() {
    angular.element(document).ready(function() {
      angular.element('.notebook-list-dropdown').on('show.bs.dropdown', function() {
        $scope.isDrawNavbarNoteList = true;
      });

      angular.element('.notebook-list-dropdown').on('hide.bs.dropdown', function() {
        $scope.isDrawNavbarNoteList = false;
      });
    });
  }
}
<commit_msg>[ZEPPELIN-2072] Home Screen doesn't work on shiro.<commit_after>/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

angular.module('zeppelinWebApp').controller('NavCtrl', NavCtrl);

NavCtrl.$inject = [
  '$scope',
  '$rootScope',
  '$http',
  '$routeParams',
  '$location',
  'noteListDataFactory',
  'baseUrlSrv',
  'websocketMsgSrv',
  'arrayOrderingSrv',
  'searchService',
  'TRASH_FOLDER_ID'
];

function NavCtrl($scope, $rootScope, $http, $routeParams, $location,
                 noteListDataFactory, baseUrlSrv, websocketMsgSrv,
                 arrayOrderingSrv, searchService, TRASH_FOLDER_ID) {
  var vm = this;
  vm.arrayOrderingSrv = arrayOrderingSrv;
  vm.connected = websocketMsgSrv.isConnected();
  vm.isActive = isActive;
  vm.logout = logout;
  vm.notes = noteListDataFactory;
  vm.search = search;
  vm.searchForm = searchService;
  vm.showLoginWindow = showLoginWindow;
  vm.TRASH_FOLDER_ID = TRASH_FOLDER_ID;
  vm.isFilterNote = isFilterNote;

  $scope.query = {q: ''};

  initController();

  function getZeppelinVersion() {
    $http.get(baseUrlSrv.getRestApiBase() + '/version').success(
      function(data, status, headers, config) {
        $rootScope.zeppelinVersion = data.body;
      }).error(
      function(data, status, headers, config) {
        console.log('Error %o %o', status, data.message);
      });
  }

  function initController() {
    $scope.isDrawNavbarNoteList = false;
    angular.element('#notebook-list').perfectScrollbar({suppressScrollX: true});

    angular.element(document).click(function() {
      $scope.query.q = '';
    });

    getZeppelinVersion();
    loadNotes();
  }

  function isFilterNote(note) {
    if (!$scope.query.q) {
      return true;
    }

    var noteName = note.name;
    if (noteName.toLowerCase().indexOf($scope.query.q.toLowerCase()) > -1) {
      return true;
    }
    return false;
  }

  function isActive(noteId) {
    return ($routeParams.noteId === noteId);
  }

  function listConfigurations() {
    websocketMsgSrv.listConfigurations();
  }

  function loadNotes() {
    websocketMsgSrv.getNoteList();
  }

  function getHomeNote(){
    websocketMsgSrv.getHomeNote();
  }

  function logout() {
    var logoutURL = baseUrlSrv.getRestApiBase() + '/login/logout';

    //for firefox and safari
    logoutURL = logoutURL.replace('//', '//false:false@');
    $http.post(logoutURL).error(function() {
      //force authcBasic (if configured) to logout
      $http.post(logoutURL).error(function() {
        $rootScope.userName = '';
        $rootScope.ticket.principal = '';
        $rootScope.ticket.ticket = '';
        $rootScope.ticket.roles = '';
        BootstrapDialog.show({
          message: 'Logout Success'
        });
        setTimeout(function() {
          window.location.replace('/');
        }, 1000);
      });
    });
  }

  function search(searchTerm) {
    $location.path('/search/' + searchTerm);
  }

  function showLoginWindow() {
    setTimeout(function() {
      angular.element('#userName').focus();
    }, 500);
  }

  /*
   ** $scope.$on functions below
   */

  $scope.$on('setNoteMenu', function(event, notes) {
    noteListDataFactory.setNotes(notes);
    initNotebookListEventListener();
  });

  $scope.$on('setConnectedStatus', function(event, param) {
    vm.connected = param;
  });

  $scope.$on('loginSuccess', function(event, param) {
    listConfigurations();
    loadNotes();
    getHomeNote();
  });

  /*
   ** Performance optimization for Browser Render.
   */
  function initNotebookListEventListener() {
    angular.element(document).ready(function() {
      angular.element('.notebook-list-dropdown').on('show.bs.dropdown', function() {
        $scope.isDrawNavbarNoteList = true;
      });

      angular.element('.notebook-list-dropdown').on('hide.bs.dropdown', function() {
        $scope.isDrawNavbarNoteList = false;
      });
    });
  }
}
<|endoftext|>


<commit_before>// package metadata file for Meteor.js

/* jshint strict:false */
/* global Package:true */

Package.describe({
    name: 'mark125:jsmaps',
    summary: 'A javascript map, and also with an abstraction layer for javascript mapping apis.',
    version: '0.0.14',
    git: 'https://github.com/markkr125/jsmaps.git',
    documentation: 'README.md'
});

Package.onUse(function (api) {
    api.versionsFrom(['METEOR@0.9.0', 'METEOR@1.0', 'METEOR@1.2']);

    var assets = [
        'library/core.js',
        'library/core.abstract-helper.js',
        'library/core.abstract.js',
        'library/bing/core.bing.js',
        'library/bing/core.bing-geocoder.js',
        'library/bing/core.bing-staticmap.js',
        'library/google/core.google.js',
        'library/google/core.google-geocoder.js',
        'library/google/core.google-staticmap.js',
        'library/here/core.here.js',
        'library/here/core.here-geocoder.js',
        'library/here/core.here-staticmap.js',
        'library/here/core.here-vector.js',
        'library/native/core.native.css',
        'library/native/core.native.js',
        'library/native/core.native-helper.js',
        'library/native/core.native-infoWindow.js',
        'library/native/core.native-marker.js',
        'library/native/core.native-ui.js',
        'library/native/core.native-vector.js',
        'library/yandex/core.yandex.js',
        'library/yandex/core.yandex-geocoder.js',
        'library/yandex/core.yandex-staticmap.js'
    ];

    if (api.addAssets) {
        api.addAssets(assets, 'client');
    } else {
        api.addFiles(assets, 'client', { isAsset: true });
    }
});

<commit_msg>Version #0.0.15<commit_after>// package metadata file for Meteor.js

/* jshint strict:false */
/* global Package:true */

Package.describe({
    name: 'mark125:jsmaps',
    summary: 'A javascript map, and also with an abstraction layer for javascript mapping apis.',
    version: '0.0.15',
    git: 'https://github.com/markkr125/jsmaps.git',
    documentation: 'README.md'
});

Package.onUse(function (api) {
    api.versionsFrom(['METEOR@0.9.0', 'METEOR@1.0', 'METEOR@1.2']);

    var assets = [
        'library/core.js',
        'library/core.abstract-helper.js',
        'library/core.abstract.js',
        'library/bing/core.bing.js',
        'library/bing/core.bing-geocoder.js',
        'library/bing/core.bing-staticmap.js',
        'library/google/core.google.js',
        'library/google/core.google-geocoder.js',
        'library/google/core.google-staticmap.js',
        'library/here/core.here.js',
        'library/here/core.here-geocoder.js',
        'library/here/core.here-staticmap.js',
        'library/here/core.here-vector.js',
        'library/native/core.native.css',
        'library/native/core.native.js',
        'library/native/core.native-helper.js',
        'library/native/core.native-infoWindow.js',
        'library/native/core.native-marker.js',
        'library/native/core.native-ui.js',
        'library/native/core.native-vector.js',
        'library/yandex/core.yandex.js',
        'library/yandex/core.yandex-geocoder.js',
        'library/yandex/core.yandex-staticmap.js'
    ];

    if (api.addAssets) {
        api.addAssets(assets, 'client');
    } else {
        api.addFiles(assets, 'client', { isAsset: true });
    }
});

<|endoftext|>


<commit_before>/*
* jQuery Mobile Framework : "click" plugin
* Copyright (c) jQuery Project
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*/
(function($, window, document, undefined) {

var nextSequencerID = 0;

function MouseEventSequencer()
{
	this.seqID = "MSE-" + nextSequencerID++;
	this.handlerDict = {};
	this.mode = null;
}

$.extend(MouseEventSequencer.prototype, {
	bind: function(ele, eventType, handler)
	{
		var $ele = $(ele),
			hArray = handlerDict[eventType];
		if (!hArray)
			hArray = handlerDict[eventType] = [];
		hArray.push(handler);

		// We always register for touchstart and mousedown because
		// we may need to synthesize some events when dealing with
		// touch devices.

		$ele.bind("touchstart." + this.seqID + " mousedown." + this.seqID, function(e,d){ self.handleDown(e, d); });
	},

	unbind: function(ele, eventType, handler)
	{
		var $ele = $(ele),
			hArray = handlerDict[eventType];
		if (hArray){
			var i = $.inArray(handler, hArray);
			if (i != -1)
				hArray.splice(i, 1);
		}
			hArray = handlerDict[eventType] = [];
		hArray.push(handler);
	},

	trigger: function(eventType, data)
	{
	},

	handleDown: function(event, data)
	{
		this.mode = isTouchEvent(event.type) ? "touch" : "mouse";
		this.trigger("mmouseover");
		this.trigger("mmousedown");
	},

	handleUp: function()
	{
		this.trigger("mmouseup");
		if (this.mode == "touch"){
			this.trigger("mclick");
			this.trigger("mmouseout");
		}
	},

	handleMove: function()
	{
		this.trigger("mmousemove");
	},

	handleOver: function()
	{
		if (this.mode != "touch")
			this.trigger("mmouseover");
	},

	handleOut: function()
	{
		if (this.mode != "touch")
			this.trigger("mmouseout");
	}
});

function isMouseEvent(eventType)
{
	eventType = eventType || "";
	return eventType.search(/^((mouse(down|up|move|over|out))|click)$/) != -1;
}

function isTouchEvent(eventType)
{
	eventType = eventType || "";
	return eventType.search(/^touch(start|end|move|cancel)$/) != -1;
}

$.extend($.fn, {
	mBind: function(eventType, handler)
	{
		if (isMouseEvent(eventType) || isTouchEvent(eventType)){
			this.each(function(){
				var $this = $(this);
				var seq = $this.data("mouseEventSequencer");
				if (!seq){
					seq = new MouseEventSequencer();
					$this.data("mouseEventSequencer", seq);
				}
				seq.bind(this, eventType, handler);
			});
		}
		else {
			this.bind(eventType, handler);
		}
		return this;
	},

	mUnbind: function(eventType, handler)
	{
		if (isMouseEvent(eventType) || isTouchEvent(eventType)){
			this.each(function(){
				var $this = $(this);
				var seq = $this.data("mouseEventSequencer");
				if (seq){
					seq.unbind(this, eventType, handler);
				}
			});
		}
		else {
			this.unbind(eventType, handler);
		}
		return this;
	}
});

})(jQuery, window, document);<commit_msg>More work-in-progress changes.<commit_after>/*
* jQuery Mobile Framework : "click" plugin
* Copyright (c) jQuery Project
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*/

// This plugin is an experient for abstracting away the touch and mouse
// events so that developers don't have to worry about which method of input
// the device their document is loaded on supports.
//
// The idea here is to allow the developer to register listeners for the
// basic mouse events, such as mousedown, mousemove, mouseup, and click,
// and the plugin will take care of registering the correct listeners
// behind the scenes to invoke the listener at the fastest possible time
// for that device, while still retaining the order of event firing in
// the traditional mouse environment, should multiple handlers be registered
// on the same element for different events.
//
// The current version simply adds mBind and mUnbind to the $.fn space,
// but we're considering other methods for making this easier. One alternative
// would be to allow users to use virtual mouse event names, such as
// "vmousedown", "vmouseup", etc, to trigger the traditional jQuery special/custom
// event api, which would then trigger this same code.

(function($, window, document, undefined) {

var nextSequencerID = 0;

var eventToSequencerName = {
	touchstart: "mmousedown",
	touchmove:  "mmousemove",
	touchend:   "mmouseup",
	mousedown:  "mmousedown",
	mousemove:  "mmousemove",
	mouseup:    "mmouseup",
	mouseover:  "mmouseover",
	mouseout:   "mmouseout",
	click:      "mclick"
};

var downEvents = "touchstart mousedown";
var upEvents   = "touchend mouseup";
var moveEvents = "touchmove mousemove";

function MouseEventSequencer()
{
	this.seqID = "MSE-" + nextSequencerID++;
	this.handlerDict = {};
	this.mode = null;
	this.downFunc = null;
}

$.extend(MouseEventSequencer.prototype, {
	bind: function(ele, eventType, handler)
	{
		var self = this,
			seqName = eventToSequencerName[eventType],
			$ele = $(ele),
			hArray = handlerDict[seqName];

		if (!hArray) {
			hArray = handlerDict[seqName] = [];
		}
		hArray.push(handler);

		if (!this.downFunc){
			// We always register for touchstart and mousedown because
			// we may need to synthesize some events when dealing with
			// touch devices.

			this.downFunc = function(e,d){ self.handleDown(e, d); }
			$ele.bind("touchstart." + this.seqID + " mousedown." + this.seqID, this.downFunc);
		}
	},

	unbind: function(ele, eventType, handler)
	{
		var $ele = $(ele),
			hArray = handlerDict[eventType];
		if (hArray){
			var i = $.inArray(handler, hArray);
			if (i != -1)
				hArray.splice(i, 1);
		}
			hArray = handlerDict[eventType] = [];
		hArray.push(handler);
	},

	trigger: function(eventType, data)
	{
	},

	handleDown: function(event, data)
	{
		this.mode = this.getModeFromType(event.type);
		if (this.mode == "touch") {
			this.trigger("mmouseover");
		}
		this.trigger("mmousedown");
	},

	handleUp: function()
	{
		this.trigger("mmouseup");
		if (this.mode == "touch"){
			this.trigger("mclick");
			this.trigger("mmouseout");
		}
	},

	handleMove: function()
	{
		this.trigger("mmousemove");
	},

	handleOver: function()
	{
		if (this.mode != "touch")
			this.trigger("mmouseover");
	},

	handleOut: function()
	{
		if (this.mode != "touch")
			this.trigger("mmouseout");
	},

	getModeFromType: function (eventType)
	{
		return isTouchEvent(eventType) ? "touch" : "mouse";
	}
});

function isMouseEvent(eventType)
{
	eventType = eventType || "";
	return eventType.search(/^((mouse(down|up|move|over|out))|click)$/) != -1;
}

function isTouchEvent(eventType)
{
	eventType = eventType || "";
	return eventType.search(/^touch(start|end|move|cancel)$/) != -1;
}

$.extend($.fn, {
	mBind: function(eventType, handler)
	{
		if (isMouseEvent(eventType) || isTouchEvent(eventType)){
			this.each(function(){
				var $this = $(this);
				var seq = $this.data("mouseEventSequencer");
				if (!seq){
					seq = new MouseEventSequencer();
					$this.data("mouseEventSequencer", seq);
				}
				seq.bind(this, eventType, handler);
			});
		}
		else {
			this.bind(eventType, handler);
		}
		return this;
	},

	mUnbind: function(eventType, handler)
	{
		if (isMouseEvent(eventType) || isTouchEvent(eventType)){
			this.each(function(){
				var $this = $(this);
				var seq = $this.data("mouseEventSequencer");
				if (seq){
					seq.unbind(this, eventType, handler);
				}
			});
		}
		else {
			this.unbind(eventType, handler);
		}
		return this;
	}
});

})(jQuery, window, document);<|endoftext|>


<commit_before>/* global FileReader */

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import ProfilePic from 'components/ProfilePic'
import Button from 'components/Button'
import ImageEditModal from 'components/Modals/ImageEditModal'
import BioEditModal from 'components/Modals/BioEditModal'
import {
  removeStatusMsg,
  updateStatusMsg,
  uploadProfilePic,
  uploadBio
} from 'redux/actions/UserActions'
import { openDirectMessageChannel } from 'redux/actions/ChatActions'
import AlertModal from 'components/Modals/AlertModal'
import { connect } from 'react-redux'
import { withRouter } from 'react-router'
import { borderRadius, Color, mobileMaxWidth } from 'constants/css'
import { css } from 'emotion'
import { loadComments } from 'helpers/requestHelpers'
import { timeSince } from 'helpers/timeStampHelpers'
import RankBar from 'components/RankBar'
import Icon from 'components/Icon'
import Comments from 'components/Comments'
import Link from 'components/Link'
import UserDetails from 'components/UserDetails'
import { socket } from 'constants/io'

class ProfilePanel extends Component {
  static propTypes = {
    expandable: PropTypes.bool,
    history: PropTypes.object,
    isCreator: PropTypes.bool,
    isProfilePage: PropTypes.bool,
    updateStatusMsg: PropTypes.func,
    openDirectMessageChannel: PropTypes.func,
    profile: PropTypes.object,
    removeStatusMsg: PropTypes.func,
    userId: PropTypes.number,
    uploadBio: PropTypes.func,
    uploadProfilePic: PropTypes.func
  }

  state = {
    comments: [],
    commentsShown: false,
    commentsLoadMoreButton: false,
    confirmModalShown: false,
    imageUri: null,
    processing: false,
    imageEditModalShown: false,
    mouseEnteredProfile: false,
    bioEditModalShown: false,
    alertModalShown: false
  }

  async componentDidMount() {
    const { isProfilePage, profile } = this.props
    try {
      const { comments, loadMoreButton } = await loadComments({
        id: profile.id,
        type: 'user',
        limit: isProfilePage ? 3 : 1
      })
      this.setState({
        comments,
        ...(isProfilePage ? { commentsLoadMoreButton: loadMoreButton } : {})
      })
    } catch (error) {
      console.error(error)
    }
  }

  async componentDidUpdate(prevProps) {
    const { isProfilePage, profile } = this.props
    if (profile.id !== prevProps.profile.id) {
      try {
        const { comments, loadMoreButton } = await loadComments({
          id: profile.id,
          type: 'user',
          limit: isProfilePage ? 3 : 1
        })
        this.setState({
          comments,
          ...(isProfilePage ? { commentsLoadMoreButton: loadMoreButton } : {})
        })
      } catch (error) {
        console.error(error)
      }
    }
  }

  render() {
    const {
      comments,
      commentsShown,
      commentsLoadMoreButton,
      imageUri,
      imageEditModalShown,
      bioEditModalShown,
      alertModalShown,
      mouseEnteredProfile,
      processing
    } = this.state
    const {
      history,
      profile,
      userId,
      expandable,
      isCreator,
      isProfilePage,
      openDirectMessageChannel,
      updateStatusMsg,
      uploadBio
    } = this.props
    const canEdit = userId === profile.id || isCreator
    const { profileFirstRow, profileSecondRow, profileThirdRow } = profile
    const noProfile = !profileFirstRow && !profileSecondRow && !profileThirdRow
    return (

        <div
          className={css`
            background: #fff;
            display: flex;
            flex-direction: column;
            padding: 1rem;
            border: #e7e7e7 1px solid;
            border-top-left-radius: ${borderRadius};
            border-top-right-radius: ${borderRadius};
            ${
              profile.twinkleXP
                ? 'border-bottom: none;'
                : `
                border-bottom-left-radius: ${borderRadius};
                border-bottom-right-radius: ${borderRadius};
              `
            }
            @media (max-width: ${mobileMaxWidth}) {
              border-radius: 0;
              border-left: none;
              border-right: none;
            }
          `}
        >

                  this.setState({ mouseEnteredProfile: true })
                }
                onMouseLeave={() =>
                  this.setState({ mouseEnteredProfile: false })
                }
              >
                <Link to={isProfilePage ? null : `/users/${profile.username}`}>
                  <ProfilePic
                    style={{ width: '18rem', height: '18rem' }}
                    userId={profile.id}
                    profilePicId={profile.profilePicId}
                    online={userId === profile.id || !!profile.online}
                    large
                  />
                </Link>

              {!isProfilePage && (

                  <Button
                    warning
                    transparent
                    style={{ color: mouseEnteredProfile && Color.orange() }}
                    onClick={() => history.push(`/users/${profile.username}`)}
                  >
                    View Profile
                  </Button>

              )}

              <UserDetails
                profile={profile}
                isProfilePage={isProfilePage}
                updateStatusMsg={updateStatusMsg}
                uploadBio={uploadBio}
                userId={userId}
              />
              {canEdit && (

                    <Button
                      transparent
                      onClick={this.onChangeProfilePictureClick}
                    >
                      Change Pic
                    </Button>
                    <Button
                      transparent
                      onClick={() => this.setState({ bioEditModalShown: true })}
                      style={{ marginLeft: '0.5rem' }}
                    >
                      Edit Bio
                    </Button>
                    {profile.id === userId &&
                      comments.length > 0 &&
                      !isProfilePage &&
                      this.renderMessagesButton({
                        style: { marginLeft: '0.5rem' }
                      })}

              )}
              {expandable &&
                userId !== profile.id && (

                    {this.renderMessagesButton()}
                    <Button
                      style={{ marginLeft: '0.5rem' }}
                      success
                      onClick={() =>
                        openDirectMessageChannel(
                          { userId },
                          { userId: profile.id, username: profile.username },
                          false
                        )
                      }
                    >
                      <Icon icon="comments" />
                      Talk
                    </Button>

                )}
              {profile.lastActive &&
                !profile.online &&
                profile.id !== userId && (

last online {timeSince(profile.lastActive)}

                )}

            <input
              ref={ref => {
                this.fileInput = ref
              }}
              style={{ display: 'none' }}
              type="file"
              onChange={this.handlePicture}
              accept="image/*"
            />
            {bioEditModalShown && (
              <BioEditModal
                firstLine={profileFirstRow}
                secondLine={profileSecondRow}
                thirdLine={profileThirdRow}
                onSubmit={this.uploadBio}
                onHide={() =>
                  this.setState({
                    bioEditModalShown: false
                  })
                }
              />
            )}
            {imageEditModalShown && (
              <ImageEditModal
                imageUri={imageUri}
                onHide={() =>
                  this.setState({
                    imageUri: null,
                    imageEditModalShown: false,
                    processing: false
                  })
                }
                processing={processing}
                onConfirm={this.uploadImage}
              />
            )}

          <Comments
            autoExpand={isProfilePage}
            autoFocus
            comments={comments}
            commentsLoadLimit={20}
            commentsShown={commentsShown}
            contentId={profile.id}
            inputAreaInnerRef={ref => (this.CommentInputArea = ref)}
            inputTypeLabel={`message to ${profile.username}`}
            loadMoreButton={commentsLoadMoreButton}
            loadMoreComments={this.onLoadMoreComments}
            noInput={profile.id === userId}
            numPreviews={1}
            onAttachStar={this.onAttachStar}
            onCommentSubmit={this.onCommentSubmit}
            onDelete={this.onDeleteComment}
            onEditDone={this.onEditComment}
            onLikeClick={this.onLikeComment}
            onLoadMoreReplies={this.onLoadMoreReplies}
            onReplySubmit={this.onReplySubmit}
            onRewardCommentEdit={this.onEditRewardComment}
            parent={{ ...profile, type: 'user' }}
            style={{ marginTop: '1rem' }}
            userId={userId}
          />

        {!!profile.twinkleXP && <RankBar profile={profile} />}
        {alertModalShown && (
          <AlertModal
            title="Image is too large (limit: 5mb)"
            content="Please select a smaller image"
            onHide={() => this.setState({ alertModalShown: false })}
          />
        )}

    )
  }

  handlePicture = event => {
    const reader = new FileReader()
    const maxSize = 5000
    const file = event.target.files[0]
    if (file.size / 1000 > maxSize) {
      return this.setState({ alertModalShown: true })
    }
    reader.onload = upload => {
      this.setState({
        imageEditModalShown: true,
        imageUri: upload.target.result
      })
    }

    reader.readAsDataURL(file)
    event.target.value = null
  }

  onAttachStar = star => {
    this.setState(state => ({
      comments: state.comments.map(comment => {
        return {
          ...comment,
          stars:
            comment.id === star.contentId
              ? (comment.stars || []).concat(star)
              : comment.stars || [],
          replies: comment.replies.map(reply => ({
            ...reply,
            stars:
              reply.id === star.contentId
                ? (reply.stars || []).concat(star)
                : reply.stars || []
          }))
        }
      })
    }))
  }

  onCommentSubmit = comment => {
    socket.emit('new_upload')
    this.setState(state => ({
      comments: [comment].concat(state.comments)
    }))
  }

  onChangeProfilePictureClick = () => {
    this.fileInput.click()
  }

  onDeleteComment = commentId => {
    this.setState(state => {
      const comments = state.comments.filter(
        comment => comment.id !== commentId
      )
      return {
        comments: comments.map(comment => ({
          ...comment,
          replies: (comment.replies || []).filter(
            reply => reply.id !== commentId
          )
        }))
      }
    })
  }

  onEditComment = ({ editedComment, commentId }) => {
    this.setState(state => {
      return {
        comments: state.comments.map(comment => ({
          ...comment,
          content: comment.id === commentId ? editedComment : comment.content,
          replies: comment.replies
            ? comment.replies.map(
                reply =>
                  reply.id === commentId
                    ? {
                        ...reply,
                        content: editedComment
                      }
                    : reply
              )
            : []
        }))
      }
    })
  }

  onEditRewardComment = ({ id, text }) => {
    this.setState(state => ({
      comments: state.comments.map(comment => ({
        ...comment,
        stars: comment.stars
          ? comment.stars.map(star => ({
              ...star,
              rewardComment: star.id === id ? text : star.rewardComment
            }))
          : [],
        replies: comment.replies.map(reply => ({
          ...reply,
          stars: reply.stars
            ? reply.stars.map(star => ({
                ...star,
                rewardComment: star.id === id ? text : star.rewardComment
              }))
            : []
        }))
      }))
    }))
  }

  onExpandComments = async() => {
    const { commentsShown, isProfilePage, profile, userId } = this.props
    if (!commentsShown && !isProfilePage) {
      const { comments, loadMoreButton } = await loadComments({
        id: profile.id,
        type: 'user',
        limit: 5
      })
      this.setState({
        comments,
        commentsShown: true,
        commentsLoadMoreButton: loadMoreButton
      })
    }
    if (profile.id !== userId) this.CommentInputArea.focus()
  }

  onLikeComment = ({ commentId, likes }) => {
    this.setState(state => {
      return {
        comments: state.comments.map(comment => ({
          ...comment,
          likes: comment.id === commentId ? likes : comment.likes,
          replies: comment.replies
            ? comment.replies.map(
                reply =>
                  reply.id === commentId
                    ? {
                        ...reply,
                        likes
                      }
                    : reply
              )
            : []
        }))
      }
    })
  }

  onLoadMoreComments = ({ comments, loadMoreButton }) => {
    this.setState(state => ({
      comments: state.comments.concat(comments),
      commentsLoadMoreButton: loadMoreButton
    }))
  }

  onLoadMoreReplies = ({ commentId, replies, loadMoreButton }) => {
    this.setState(state => ({
      comments: state.comments.map(comment => ({
        ...comment,
        replies:
          comment.id === commentId
            ? replies.concat(comment.replies)
            : comment.replies,
        loadMoreButton:
          comment.id === commentId ? loadMoreButton : comment.loadMoreButton
      }))
    }))
  }

  onReplySubmit = data => {
    this.setState(state => ({
      comments: state.comments.map(comment => {
        let match = false
        let commentId = data.replyId || data.commentId
        if (comment.id === commentId) {
          match = true
        } else {
          for (let reply of comment.replies || []) {
            if (reply.id === commentId) {
              match = true
              break
            }
          }
        }
        return {
          ...comment,
          replies: match ? comment.replies.concat([data]) : comment.replies
        }
      })
    }))
  }

  renderMessagesButton = (props = {}) => {
    const {
      isProfilePage,
      profile: { id, numMessages },
      userId
    } = this.props
    const { commentsShown } = this.state
    return (
      <Button
        {...props}
        disabled={commentsShown && id === userId}
        logo
        onClick={this.onExpandComments}
      >
        <Icon icon="comment-alt" />

          {id === userId ? '' : 'Leave '}
          Message
          {id === userId && Number(numMessages) > 0 && !commentsShown
            ? `${numMessages > 1 ? 's' : ''}`
            : ''}
          {Number(numMessages) > 0 && !commentsShown && !isProfilePage
            ? ` (${numMessages})`
            : ''}

      </Button>
    )
  }

  uploadBio = async params => {
    const { profile, uploadBio } = this.props
    await uploadBio({ ...params, profileId: profile.id })
    this.setState({
      bioEditModalShown: false
    })
  }

  uploadImage = async image => {
    const { uploadProfilePic } = this.props
    this.setState({
      processing: true
    })
    await uploadProfilePic(image)
    this.setState({
      imageUri: null,
      processing: false,
      imageEditModalShown: false
    })
  }
}

export default connect(
  state => ({
    isCreator: state.UserReducer.isCreator,
    userId: state.UserReducer.userId
  }),
  {
    removeStatusMsg,
    updateStatusMsg,
    uploadProfilePic,
    uploadBio,
    openDirectMessageChannel
  }
)(withRouter(ProfilePanel))
<commit_msg>minor fix<commit_after>/* global FileReader */

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import ProfilePic from 'components/ProfilePic'
import Button from 'components/Button'
import ImageEditModal from 'components/Modals/ImageEditModal'
import BioEditModal from 'components/Modals/BioEditModal'
import {
  removeStatusMsg,
  updateStatusMsg,
  uploadProfilePic,
  uploadBio
} from 'redux/actions/UserActions'
import { openDirectMessageChannel } from 'redux/actions/ChatActions'
import AlertModal from 'components/Modals/AlertModal'
import { connect } from 'react-redux'
import { withRouter } from 'react-router'
import { borderRadius, Color, mobileMaxWidth } from 'constants/css'
import { css } from 'emotion'
import { loadComments } from 'helpers/requestHelpers'
import { timeSince } from 'helpers/timeStampHelpers'
import RankBar from 'components/RankBar'
import Icon from 'components/Icon'
import Comments from 'components/Comments'
import Link from 'components/Link'
import UserDetails from 'components/UserDetails'
import { socket } from 'constants/io'

class ProfilePanel extends Component {
  static propTypes = {
    expandable: PropTypes.bool,
    history: PropTypes.object,
    isCreator: PropTypes.bool,
    isProfilePage: PropTypes.bool,
    updateStatusMsg: PropTypes.func,
    openDirectMessageChannel: PropTypes.func,
    profile: PropTypes.object,
    removeStatusMsg: PropTypes.func,
    userId: PropTypes.number,
    uploadBio: PropTypes.func,
    uploadProfilePic: PropTypes.func
  }

  state = {
    comments: [],
    commentsShown: false,
    commentsLoadMoreButton: false,
    confirmModalShown: false,
    imageUri: null,
    processing: false,
    imageEditModalShown: false,
    mouseEnteredProfile: false,
    bioEditModalShown: false,
    alertModalShown: false
  }

  async componentDidMount() {
    const { isProfilePage, profile } = this.props
    try {
      const { comments, loadMoreButton } = await loadComments({
        id: profile.id,
        type: 'user',
        limit: isProfilePage ? 3 : 1
      })
      this.setState({
        comments,
        ...(isProfilePage ? { commentsLoadMoreButton: loadMoreButton } : {})
      })
    } catch (error) {
      console.error(error)
    }
  }

  async componentDidUpdate(prevProps) {
    const { isProfilePage, profile } = this.props
    if (profile.id !== prevProps.profile.id) {
      try {
        const { comments, loadMoreButton } = await loadComments({
          id: profile.id,
          type: 'user',
          limit: isProfilePage ? 3 : 1
        })
        this.setState({
          comments,
          ...(isProfilePage ? { commentsLoadMoreButton: loadMoreButton } : {})
        })
      } catch (error) {
        console.error(error)
      }
    }
  }

  render() {
    const {
      comments,
      commentsShown,
      commentsLoadMoreButton,
      imageUri,
      imageEditModalShown,
      bioEditModalShown,
      alertModalShown,
      mouseEnteredProfile,
      processing
    } = this.state
    const {
      history,
      profile,
      userId,
      expandable,
      isCreator,
      isProfilePage,
      openDirectMessageChannel,
      updateStatusMsg,
      uploadBio
    } = this.props
    const canEdit = userId === profile.id || isCreator
    const { profileFirstRow, profileSecondRow, profileThirdRow } = profile
    const noProfile = !profileFirstRow && !profileSecondRow && !profileThirdRow
    return (

        <div
          className={css`
            background: #fff;
            display: flex;
            flex-direction: column;
            padding: 1rem;
            border: #e7e7e7 1px solid;
            border-top-left-radius: ${borderRadius};
            border-top-right-radius: ${borderRadius};
            ${
              profile.twinkleXP
                ? 'border-bottom: none;'
                : `
                border-bottom-left-radius: ${borderRadius};
                border-bottom-right-radius: ${borderRadius};
              `
            }
            @media (max-width: ${mobileMaxWidth}) {
              border-radius: 0;
              border-left: none;
              border-right: none;
            }
          `}
        >

                  this.setState({ mouseEnteredProfile: true })
                }
                onMouseLeave={() =>
                  this.setState({ mouseEnteredProfile: false })
                }
              >
                <Link to={isProfilePage ? null : `/users/${profile.username}`}>
                  <ProfilePic
                    style={{ width: '18rem', height: '18rem' }}
                    userId={profile.id}
                    profilePicId={profile.profilePicId}
                    online={userId === profile.id || !!profile.online}
                    large
                  />
                </Link>

              {!isProfilePage && (

                  <Button
                    warning
                    transparent
                    style={{ color: mouseEnteredProfile && Color.orange() }}
                    onClick={() => history.push(`/users/${profile.username}`)}
                  >
                    View Profile
                  </Button>

              )}

              <UserDetails
                profile={profile}
                isProfilePage={isProfilePage}
                updateStatusMsg={updateStatusMsg}
                uploadBio={uploadBio}
                userId={userId}
              />
              {canEdit && (

                    <Button
                      transparent
                      onClick={this.onChangeProfilePictureClick}
                    >
                      Change Pic
                    </Button>
                    <Button
                      transparent
                      onClick={() => this.setState({ bioEditModalShown: true })}
                      style={{ marginLeft: '0.5rem' }}
                    >
                      Edit Bio
                    </Button>
                    {profile.id === userId &&
                      comments.length > 0 &&
                      !isProfilePage &&
                      this.renderMessagesButton({
                        style: { marginLeft: '0.5rem' }
                      })}

              )}
              {expandable &&
                userId !== profile.id && (

                    {this.renderMessagesButton()}
                    <Button
                      style={{ marginLeft: '0.5rem' }}
                      success
                      onClick={() =>
                        openDirectMessageChannel(
                          { userId },
                          { userId: profile.id, username: profile.username },
                          false
                        )
                      }
                    >
                      <Icon icon="comments" />
                      Talk
                    </Button>

                )}
              {profile.lastActive &&
                !profile.online &&
                profile.id !== userId && (

last online {timeSince(profile.lastActive)}

                )}

            <input
              ref={ref => {
                this.fileInput = ref
              }}
              style={{ display: 'none' }}
              type="file"
              onChange={this.handlePicture}
              accept="image/*"
            />
            {bioEditModalShown && (
              <BioEditModal
                firstLine={profileFirstRow}
                secondLine={profileSecondRow}
                thirdLine={profileThirdRow}
                onSubmit={this.uploadBio}
                onHide={() =>
                  this.setState({
                    bioEditModalShown: false
                  })
                }
              />
            )}
            {imageEditModalShown && (
              <ImageEditModal
                imageUri={imageUri}
                onHide={() =>
                  this.setState({
                    imageUri: null,
                    imageEditModalShown: false,
                    processing: false
                  })
                }
                processing={processing}
                onConfirm={this.uploadImage}
              />
            )}

          <Comments
            autoExpand={isProfilePage}
            autoFocus
            comments={comments}
            commentsLoadLimit={20}
            commentsShown={commentsShown}
            contentId={profile.id}
            inputAreaInnerRef={ref => (this.CommentInputArea = ref)}
            inputTypeLabel={`message to ${profile.username}`}
            loadMoreButton={commentsLoadMoreButton}
            loadMoreComments={this.onLoadMoreComments}
            noInput={profile.id === userId}
            numPreviews={1}
            onAttachStar={this.onAttachStar}
            onCommentSubmit={this.onCommentSubmit}
            onDelete={this.onDeleteComment}
            onEditDone={this.onEditComment}
            onLikeClick={this.onLikeComment}
            onLoadMoreReplies={this.onLoadMoreReplies}
            onReplySubmit={this.onReplySubmit}
            onRewardCommentEdit={this.onEditRewardComment}
            parent={{ ...profile, type: 'user' }}
            style={{ marginTop: '1rem' }}
            userId={userId}
          />

        {!!profile.twinkleXP && <RankBar profile={profile} />}
        {alertModalShown && (
          <AlertModal
            title="Image is too large (limit: 5mb)"
            content="Please select a smaller image"
            onHide={() => this.setState({ alertModalShown: false })}
          />
        )}

    )
  }

  handlePicture = event => {
    const reader = new FileReader()
    const maxSize = 5000
    const file = event.target.files[0]
    if (file.size / 1000 > maxSize) {
      return this.setState({ alertModalShown: true })
    }
    reader.onload = upload => {
      this.setState({
        imageEditModalShown: true,
        imageUri: upload.target.result
      })
    }

    reader.readAsDataURL(file)
    event.target.value = null
  }

  onAttachStar = star => {
    this.setState(state => ({
      comments: state.comments.map(comment => {
        return {
          ...comment,
          stars:
            comment.id === star.contentId
              ? (comment.stars || []).concat(star)
              : comment.stars || [],
          replies: comment.replies.map(reply => ({
            ...reply,
            stars:
              reply.id === star.contentId
                ? (reply.stars || []).concat(star)
                : reply.stars || []
          }))
        }
      })
    }))
  }

  onCommentSubmit = comment => {
    socket.emit('new_upload')
    this.setState(state => ({
      comments: [comment].concat(state.comments)
    }))
  }

  onChangeProfilePictureClick = () => {
    this.fileInput.click()
  }

  onDeleteComment = commentId => {
    this.setState(state => {
      const comments = state.comments.filter(
        comment => comment.id !== commentId
      )
      return {
        comments: comments.map(comment => ({
          ...comment,
          replies: (comment.replies || []).filter(
            reply => reply.id !== commentId
          )
        }))
      }
    })
  }

  onEditComment = ({ editedComment, commentId }) => {
    this.setState(state => {
      return {
        comments: state.comments.map(comment => ({
          ...comment,
          content: comment.id === commentId ? editedComment : comment.content,
          replies: comment.replies
            ? comment.replies.map(
                reply =>
                  reply.id === commentId
                    ? {
                        ...reply,
                        content: editedComment
                      }
                    : reply
              )
            : []
        }))
      }
    })
  }

  onEditRewardComment = ({ id, text }) => {
    this.setState(state => ({
      comments: state.comments.map(comment => ({
        ...comment,
        stars: comment.stars
          ? comment.stars.map(star => ({
              ...star,
              rewardComment: star.id === id ? text : star.rewardComment
            }))
          : [],
        replies: comment.replies.map(reply => ({
          ...reply,
          stars: reply.stars
            ? reply.stars.map(star => ({
                ...star,
                rewardComment: star.id === id ? text : star.rewardComment
              }))
            : []
        }))
      }))
    }))
  }

  onExpandComments = async() => {
    const { commentsShown, isProfilePage, profile, userId } = this.props
    if (!commentsShown && !isProfilePage) {
      const { comments, loadMoreButton } = await loadComments({
        id: profile.id,
        type: 'user',
        limit: 5
      })
      this.setState({
        comments,
        commentsShown: true,
        commentsLoadMoreButton: loadMoreButton
      })
    }
    if (profile.id !== userId) this.CommentInputArea.focus()
  }

  onLikeComment = ({ commentId, likes }) => {
    this.setState(state => {
      return {
        comments: state.comments.map(comment => ({
          ...comment,
          likes: comment.id === commentId ? likes : comment.likes,
          replies: comment.replies
            ? comment.replies.map(
                reply =>
                  reply.id === commentId
                    ? {
                        ...reply,
                        likes
                      }
                    : reply
              )
            : []
        }))
      }
    })
  }

  onLoadMoreComments = ({ comments, loadMoreButton }) => {
    this.setState(state => ({
      comments: state.comments.concat(comments),
      commentsLoadMoreButton: loadMoreButton
    }))
  }

  onLoadMoreReplies = ({ commentId, replies, loadMoreButton }) => {
    this.setState(state => ({
      comments: state.comments.map(comment => ({
        ...comment,
        replies:
          comment.id === commentId
            ? replies.concat(comment.replies)
            : comment.replies,
        loadMoreButton:
          comment.id === commentId ? loadMoreButton : comment.loadMoreButton
      }))
    }))
  }

  onReplySubmit = data => {
    socket.emit('new_upload')
    this.setState(state => ({
      comments: state.comments.map(comment => {
        let match = false
        let commentId = data.replyId || data.commentId
        if (comment.id === commentId) {
          match = true
        } else {
          for (let reply of comment.replies || []) {
            if (reply.id === commentId) {
              match = true
              break
            }
          }
        }
        return {
          ...comment,
          replies: match ? comment.replies.concat([data]) : comment.replies
        }
      })
    }))
  }

  renderMessagesButton = (props = {}) => {
    const {
      isProfilePage,
      profile: { id, numMessages },
      userId
    } = this.props
    const { commentsShown } = this.state
    return (
      <Button
        {...props}
        disabled={commentsShown && id === userId}
        logo
        onClick={this.onExpandComments}
      >
        <Icon icon="comment-alt" />

          {id === userId ? '' : 'Leave '}
          Message
          {id === userId && Number(numMessages) > 0 && !commentsShown
            ? `${numMessages > 1 ? 's' : ''}`
            : ''}
          {Number(numMessages) > 0 && !commentsShown && !isProfilePage
            ? ` (${numMessages})`
            : ''}

      </Button>
    )
  }

  uploadBio = async params => {
    const { profile, uploadBio } = this.props
    await uploadBio({ ...params, profileId: profile.id })
    this.setState({
      bioEditModalShown: false
    })
  }

  uploadImage = async image => {
    const { uploadProfilePic } = this.props
    this.setState({
      processing: true
    })
    await uploadProfilePic(image)
    this.setState({
      imageUri: null,
      processing: false,
      imageEditModalShown: false
    })
  }
}

export default connect(
  state => ({
    isCreator: state.UserReducer.isCreator,
    userId: state.UserReducer.userId
  }),
  {
    removeStatusMsg,
    updateStatusMsg,
    uploadProfilePic,
    uploadBio,
    openDirectMessageChannel
  }
)(withRouter(ProfilePanel))
<|endoftext|>


<commit_before>/* See license.txt for terms of usage */

// ************************************************************************************************
// Constants

const CLASS_ID = Components.ID("{D2AC51BC-1622-4d4d-85CB-F8E8B5805CB9}");
const CLASS_NAME = "Firebug Trace Console Service";
const CONTRACT_ID = "@joehewitt.com/firebug-trace-service;1";
const EXTENSIONS = "extensions";
const DBG_ = "DBG_";

const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;

const PrefService = Cc["@mozilla.org/preferences-service;1"];
const prefs = PrefService.getService(Ci.nsIPrefBranch2);
const prefService = PrefService.getService(Ci.nsIPrefService);
const consoleService = Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService);

const appShellService = Components.classes["@mozilla.org/appshell/appShellService;1"].getService(Components.interfaces.nsIAppShellService);                       /*@explore*/

// ************************************************************************************************
// Service implementation

//var win = appShellService.hiddenDOMWindow;
const win = false;

function TraceConsoleService()  // singleton
{
    this.observers = [];
    this.optionMaps = {};

    // Listen for preferences changes. Trace Options can be changed at run time.
    prefs.addObserver("extensions", this, false);

    this.wrappedJSObject = this; // why not
}

TraceConsoleService.prototype =
{
	getTracer: function(prefDomain)
	{
		if (!this.optionMaps[prefDomain])
			this.optionMaps[prefDomain] = this.createManagedOptionMap(prefDomain);
		if (win)
			win.dump("TraceConsoleService.getTracer, prefDomain: "+prefDomain+"\n");
		return this.optionMaps[prefDomain];
	},

	createManagedOptionMap: function(prefDomain)
	{
		var optionMap = new TraceBase(prefDomain);

		var branch = prefService.getBranch ( prefDomain );
		var arrayDesc = {};
		var children = branch.getChildList("", arrayDesc);
		for (var i = 0; i < children.length; i++)
		{
			var p = children[i];
			var m = p.indexOf("DBG_");
			if (m != -1)
			{
				var optionName = p.substr(1); // drop leading .
				optionMap[optionName] = this.getPref(prefDomain+p);
				if (win)
					win.dump("TraceConsoleService.createManagedOptionMap "+optionName+"="+optionMap[optionName]+"\n");
			}
		}

		return optionMap;
	},

    /* nsIObserve */
    observe: function(subject, topic, data)
    {
    	if (data.substr(0,EXTENSIONS.length) == EXTENSIONS)
    	{
    		for (var prefDomain in gTraceService.optionMaps)
    		{
    			if (data.substr(0, prefDomain.length) == prefDomain)
    			{
    				var optionName = data.substr(prefDomain.length+1); // skip dot
    				if (optionName.substr(0, DBG_.length) == DBG_)
    					gTraceService.optionMaps[prefDomain][optionName] = this.getPref(data);
    				if (win)
    					win.dump("TraceConsoleService.observe, prefDomain: "+prefDomain+" optionName "+optionName+"\n");
    			}
    		}
    	}
    },

    getPref: function(prefName)
    {
        var type = prefs.getPrefType(prefName);
        if (type == Ci.nsIPrefBranch.PREF_STRING)
            return prefs.getCharPref(prefName);
        else if (type == Ci.nsIPrefBranch.PREF_INT)
            return prefs.getIntPref(prefName);
        else if (type == Ci.nsIPrefBranch.PREF_BOOL)
            return prefs.getBoolPref(prefName);
    },

    // Prepare trace-object and dispatch to all observers.
    dispatch: function(messageType, message, obj)
    {
        // Translate string object.
        if (typeof(obj) == "string") {
            var string = Cc["@mozilla.org/supports-cstring;1"].createInstance(Ci.nsISupportsCString);
            string.data = obj;
            obj = string;
        }

        // Create wrapper with message type info.
        var messageInfo = {
            obj: obj,
            type: messageType
        };
        if (win)
			win.dump("TraceConsoleService.dispatch, prefDomain: "+messageType+" message: "+message+"\n");
        // Pass JS object properly through XPConnect.
        var wrappedSubject = {wrappedJSObject: messageInfo};
        gTraceService.notifyObservers(wrappedSubject, "firebug-trace-on-message", message);
    },

    /* nsIObserverService */
    addObserver: function(observer, topic, weak)
    {
        if (topic != "firebug-trace-on-message")
            throw Cr.NS_ERROR_INVALID_ARG;

        this.observers.push(observer);
    },

    removeObserver: function(observer, topic)
    {
        if (topic != "firebug-trace-on-message")
            throw Cr.NS_ERROR_INVALID_ARG;

        for (var i=0; i < this.observers.length; i++) {
            if (this.observers[i] == observer) {
                this.observers.splice(i, 1);
                break;
            }
        }
    },

    notifyObservers: function(subject, topic, someData)
    {
        try
        {
            if (this.observers.length > 0)
            {
                for (var i=0; i<this.observers.length; i++)
                	this.observers[i].observe(subject, topic, someData);
            }
            else
            {
            	lastResort(this.observers, subject, someData);
            }
        }
        catch (err)
        {
            // If it's not possible to distribute the log through registered observers,
            // use Firefox ErrorConsole. Ulimately the trace-console listens for it
            // too and so, will display that.
            var scriptError = Cc["@mozilla.org/scripterror;1"].createInstance(Ci.nsIScriptError);
            scriptError.init("[JavaScript Error: Failed to notify firebug-trace observers!] " +
                err.toString(), err.sourceName,
                err.sourceLine, err.lineNumber, err.columnNumber, err.flags, err.category);
            consoleService.logMessage(scriptError);
        }
    },

    enumerateObservers: function(topic)
    {
        return null;
    },

	/* nsISupports */
	QueryInterface: function(iid)
	{
        if (iid.equals(Ci.nsISupports) ||
            iid.equals(Ci.nsIObserverService))
 		    return this;

		throw Cr.NS_ERROR_NO_INTERFACE;
	}
};

function lastResort(listeners, subject, someData)
{
    var hiddenWindow = appShellService.hiddenDOMWindow;
    var unwrapped = subject.wrappedJSObject;
    var objPart = unwrapped.obj ? (" obj: "+unwrapped.obj) : "";
    hiddenWindow.dump("FTS"+listeners.length+": "+someData+objPart+"\n");
}
// ************************************************************************************************
// Public TraceService API

// Prevent tracing from code that performs tracing.
var noTrace = false;

var TraceAPI = {
    dump: function(messageType, message, obj) {
        if (noTrace)
            return;

        noTrace = true;
        gTraceService.dispatch(messageType, message, obj);
        noTrace = false;
    },

    sysout: function(message, obj) {
        this.dump(null, message, obj);
    },

    // OBSOLETE
    dumpProperties: function(message, obj) {
        this.sysout(message, obj);
    },

    dumpStack: function(message) {
        this.sysout(message);
    },

    dumpEvent: function(message, eventObj) {
        this.sysout(message, eventObj);
    },

    dumpInterfaces: function(message, eventObj) {
        this.sysout(message, eventObj);
    }
};

var TraceBase = function(prefDomain) {
	this.prefDomain = prefDomain;
	this.sysout = function(message, obj) {
	    TraceAPI.dump(this.prefDomain, message, obj);
	}
}
//Derive all properties from TraceAPI
for (var p in TraceAPI)
    TraceBase.prototype[p] = TraceAPI[p];

// ************************************************************************************************
// Service factory

var gTraceService = null;
var TraceConsoleServiceFactory =
{
    createInstance: function (outer, iid)
    {
        if (outer != null)
            throw Cr.NS_ERROR_NO_AGGREGATION;

        if (iid.equals(Ci.nsISupports) ||
            iid.equals(Ci.nsIObserverService))
        {
		    if (!gTraceService)
    		    gTraceService = new TraceConsoleService();
            return gTraceService.QueryInterface(iid);
        }

        throw Cr.NS_ERROR_NO_INTERFACE;
    },

	QueryInterface: function(iid)
	{
		if (iid.equals(Ci.nsISupports) ||
		    iid.equals(Ci.nsISupportsWeakReference) ||
		    iid.equals(Ci.nsIFactory))
			return this;

		throw Cr.NS_ERROR_NO_INTERFACE;
	}
};

// ************************************************************************************************
// Module implementation

var TraceConsoleServiceModule =
{
    registerSelf: function (compMgr, fileSpec, location, type)
    {
        compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar);
        compMgr.registerFactoryLocation(CLASS_ID, CLASS_NAME,
            CONTRACT_ID, fileSpec, location, type);
    },

    unregisterSelf: function(compMgr, fileSpec, location)
    {
        compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar);
        compMgr.unregisterFactoryLocation(CLASS_ID, location);
    },

    getClassObject: function (compMgr, cid, iid)
    {
        if (!iid.equals(Ci.nsIFactory))
            throw Cr.NS_ERROR_NOT_IMPLEMENTED;

        if (cid.equals(CLASS_ID))
            return TraceConsoleServiceFactory;

        throw Cr.NS_ERROR_NO_INTERFACE;
    },

    canUnload: function(compMgr)
    {
        return true;
    }
};

// ************************************************************************************************

function NSGetModule(compMgr, fileSpec)
{
    return TraceConsoleServiceModule;
}
<commit_msg>add option extensions.firebug-tracing-service.toOSConsole<commit_after>/* See license.txt for terms of usage */

// ************************************************************************************************
// Constants

const CLASS_ID = Components.ID("{D2AC51BC-1622-4d4d-85CB-F8E8B5805CB9}");
const CLASS_NAME = "Firebug Trace Console Service";
const CONTRACT_ID = "@joehewitt.com/firebug-trace-service;1";
const EXTENSIONS = "extensions";
const DBG_ = "DBG_";

const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;

const PrefService = Cc["@mozilla.org/preferences-service;1"];
const prefs = PrefService.getService(Ci.nsIPrefBranch2);
const prefService = PrefService.getService(Ci.nsIPrefService);
const consoleService = Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService);

const appShellService = Components.classes["@mozilla.org/appshell/appShellService;1"].getService(Components.interfaces.nsIAppShellService);                       /*@explore*/

// ************************************************************************************************
// Service implementation

var win = false;

function TraceConsoleService()  // singleton
{
    this.observers = [];
    this.optionMaps = {};

    // Listen for preferences changes. Trace Options can be changed at run time.
    prefs.addObserver("extensions", this, false);

    this.wrappedJSObject = this; // why not
}

TraceConsoleService.prototype =
{
	getTracer: function(prefDomain)
	{
		if (this.getPref("extensions.firebug-tracing-service.toOSConsole"))
		{
			 win = appShellService.hiddenDOMWindow;  // also need browser.dom.window.dump.enabled true
		}

		if (!this.optionMaps[prefDomain])
			this.optionMaps[prefDomain] = this.createManagedOptionMap(prefDomain);
		if (win)
			win.dump("TraceConsoleService.getTracer, prefDomain: "+prefDomain+"\n");
		return this.optionMaps[prefDomain];
	},

	createManagedOptionMap: function(prefDomain)
	{
		var optionMap = new TraceBase(prefDomain);

		var branch = prefService.getBranch ( prefDomain );
		var arrayDesc = {};
		var children = branch.getChildList("", arrayDesc);
		for (var i = 0; i < children.length; i++)
		{
			var p = children[i];
			var m = p.indexOf("DBG_");
			if (m != -1)
			{
				var optionName = p.substr(1); // drop leading .
				optionMap[optionName] = this.getPref(prefDomain+p);
				if (win)
					win.dump("TraceConsoleService.createManagedOptionMap "+optionName+"="+optionMap[optionName]+"\n");
			}
		}

		return optionMap;
	},

    /* nsIObserve */
    observe: function(subject, topic, data)
    {
    	if (data.substr(0,EXTENSIONS.length) == EXTENSIONS)
    	{
    		for (var prefDomain in gTraceService.optionMaps)
    		{
    			if (data.substr(0, prefDomain.length) == prefDomain)
    			{
    				var optionName = data.substr(prefDomain.length+1); // skip dot
    				if (optionName.substr(0, DBG_.length) == DBG_)
    					gTraceService.optionMaps[prefDomain][optionName] = this.getPref(data);
    				if (win)
    					win.dump("TraceConsoleService.observe, prefDomain: "+prefDomain+" optionName "+optionName+"\n");
    			}
    		}
    	}
    },

    getPref: function(prefName)
    {
        var type = prefs.getPrefType(prefName);
        if (type == Ci.nsIPrefBranch.PREF_STRING)
            return prefs.getCharPref(prefName);
        else if (type == Ci.nsIPrefBranch.PREF_INT)
            return prefs.getIntPref(prefName);
        else if (type == Ci.nsIPrefBranch.PREF_BOOL)
            return prefs.getBoolPref(prefName);
    },

    // Prepare trace-object and dispatch to all observers.
    dispatch: function(messageType, message, obj)
    {
        // Translate string object.
        if (typeof(obj) == "string") {
            var string = Cc["@mozilla.org/supports-cstring;1"].createInstance(Ci.nsISupportsCString);
            string.data = obj;
            obj = string;
        }

        // Create wrapper with message type info.
        var messageInfo = {
            obj: obj,
            type: messageType
        };
        if (win)
			win.dump("TraceConsoleService.dispatch, prefDomain: "+messageType+" message: "+message+"\n");
        // Pass JS object properly through XPConnect.
        var wrappedSubject = {wrappedJSObject: messageInfo};
        gTraceService.notifyObservers(wrappedSubject, "firebug-trace-on-message", message);
    },

    /* nsIObserverService */
    addObserver: function(observer, topic, weak)
    {
        if (topic != "firebug-trace-on-message")
            throw Cr.NS_ERROR_INVALID_ARG;

        if (this.observers.length == 0) // mark where trace begins.
        	lastResort(this.observers, topic, "addObserver");

        this.observers.push(observer);
    },

    removeObserver: function(observer, topic)
    {
        if (topic != "firebug-trace-on-message")
            throw Cr.NS_ERROR_INVALID_ARG;

        for (var i=0; i < this.observers.length; i++) {
            if (this.observers[i] == observer) {
                this.observers.splice(i, 1);
                break;
            }
        }
    },

    notifyObservers: function(subject, topic, someData)
    {
        try
        {
            if (this.observers.length > 0)
            {
                for (var i=0; i<this.observers.length; i++)
                	this.observers[i].observe(subject, topic, someData);
            }
            else
            {
            	lastResort(this.observers, subject, someData);
            }
        }
        catch (err)
        {
            // If it's not possible to distribute the log through registered observers,
            // use Firefox ErrorConsole. Ulimately the trace-console listens for it
            // too and so, will display that.
            var scriptError = Cc["@mozilla.org/scripterror;1"].createInstance(Ci.nsIScriptError);
            scriptError.init("[JavaScript Error: Failed to notify firebug-trace observers!] " +
                err.toString(), err.sourceName,
                err.sourceLine, err.lineNumber, err.columnNumber, err.flags, err.category);
            consoleService.logMessage(scriptError);
        }
    },

    enumerateObservers: function(topic)
    {
        return null;
    },

	/* nsISupports */
	QueryInterface: function(iid)
	{
        if (iid.equals(Ci.nsISupports) ||
            iid.equals(Ci.nsIObserverService))
 		    return this;

		throw Cr.NS_ERROR_NO_INTERFACE;
	}
};

function lastResort(listeners, subject, someData)
{
    var hiddenWindow = appShellService.hiddenDOMWindow;
    var unwrapped = subject.wrappedJSObject;
    if (unwrapped)
    	var objPart = unwrapped.obj ? (" obj: "+unwrapped.obj) : "";
    else
    	var objPart = subject;

    hiddenWindow.dump("FTS"+listeners.length+": "+someData+objPart+"\n");
}
// ************************************************************************************************
// Public TraceService API

// Prevent tracing from code that performs tracing.
var noTrace = false;

var TraceAPI = {
    dump: function(messageType, message, obj) {
        if (noTrace)
            return;

        noTrace = true;
        gTraceService.dispatch(messageType, message, obj);
        noTrace = false;
    },

    sysout: function(message, obj) {
        this.dump(null, message, obj);
    },

    // OBSOLETE
    dumpProperties: function(message, obj) {
        this.sysout(message, obj);
    },

    dumpStack: function(message) {
        this.sysout(message);
    },

    dumpEvent: function(message, eventObj) {
        this.sysout(message, eventObj);
    },

    dumpInterfaces: function(message, eventObj) {
        this.sysout(message, eventObj);
    }
};

var TraceBase = function(prefDomain) {
	this.prefDomain = prefDomain;
	this.sysout = function(message, obj) {
	    TraceAPI.dump(this.prefDomain, message, obj);
	}
}
//Derive all properties from TraceAPI
for (var p in TraceAPI)
    TraceBase.prototype[p] = TraceAPI[p];

// ************************************************************************************************
// Service factory

var gTraceService = null;
var TraceConsoleServiceFactory =
{
    createInstance: function (outer, iid)
    {
        if (outer != null)
            throw Cr.NS_ERROR_NO_AGGREGATION;

        if (iid.equals(Ci.nsISupports) ||
            iid.equals(Ci.nsIObserverService))
        {
		    if (!gTraceService)
    		    gTraceService = new TraceConsoleService();
            return gTraceService.QueryInterface(iid);
        }

        throw Cr.NS_ERROR_NO_INTERFACE;
    },

	QueryInterface: function(iid)
	{
		if (iid.equals(Ci.nsISupports) ||
		    iid.equals(Ci.nsISupportsWeakReference) ||
		    iid.equals(Ci.nsIFactory))
			return this;

		throw Cr.NS_ERROR_NO_INTERFACE;
	}
};

// ************************************************************************************************
// Module implementation

var TraceConsoleServiceModule =
{
    registerSelf: function (compMgr, fileSpec, location, type)
    {
        compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar);
        compMgr.registerFactoryLocation(CLASS_ID, CLASS_NAME,
            CONTRACT_ID, fileSpec, location, type);
    },

    unregisterSelf: function(compMgr, fileSpec, location)
    {
        compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar);
        compMgr.unregisterFactoryLocation(CLASS_ID, location);
    },

    getClassObject: function (compMgr, cid, iid)
    {
        if (!iid.equals(Ci.nsIFactory))
            throw Cr.NS_ERROR_NOT_IMPLEMENTED;

        if (cid.equals(CLASS_ID))
            return TraceConsoleServiceFactory;

        throw Cr.NS_ERROR_NO_INTERFACE;
    },

    canUnload: function(compMgr)
    {
        return true;
    }
};

// ************************************************************************************************

function NSGetModule(compMgr, fileSpec)
{
    return TraceConsoleServiceModule;
}
<|endoftext|>