<commit_before>'use strict';

var RedisConnector = require('./js/service/redis-connector'),
    RedisCommands = require('./js/service/redis-commands');

Polymer({
	keys: {},
    path: [],
    redisCommands: {},
    redisConnector: new RedisConnector(),
    ready: function() {
        var self = this;

        self.redisConnector.on('ready', function (client) {
            self.redisCommands = new RedisCommands(client);

            self.redisCommands.fetchKeys(client, function (err, replies) {
                self.setKeys(replies);
            });

            client.on('error', function (err) {
                console.log('Error ' + err);
            });
        });
    },
    selectDB: function (event) {
        var self = this;

        var client = self.redisConnector.getConnection();
        client.select(event.detail, function () {

            self.redisCommands.fetchKeys(client, function (err, replies) {
                self.setKeys(replies);
            });
        });
    },
    setKeys: function (keys) {
        this.keys = this.redisCommands.toTree(keys);
        this.path = [];
        this.path.push(this.keys);
    },
	getKeys: function (o) {
	    return Object.keys(o);
	},
    openWord: function (event, detail, sender) {
        var word = sender.getAttribute('data-word'),
            col = parseInt(sender.getAttribute('data-col')),
            nextPath;

        this.path = this.path.slice(0, col + 1);

        if (this.path[this.path.length - 1] && this.path[this.path.length - 1][word]) {
            nextPath = this.path[this.path.length - 1][word];

            if (Object.keys(nextPath).length) {
                this.path.push(nextPath);
            }
        }
    }
});<commit_msg>Forgot a radix<commit_after>'use strict';

var RedisConnector = require('./js/service/redis-connector'),
    RedisCommands = require('./js/service/redis-commands');

Polymer({
	keys: {},
    path: [],
    redisCommands: {},
    redisConnector: new RedisConnector(),
    ready: function() {
        var self = this;

        self.redisConnector.on('ready', function (client) {
            self.redisCommands = new RedisCommands(client);

            self.redisCommands.fetchKeys(client, function (err, replies) {
                self.setKeys(replies);
            });

            client.on('error', function (err) {
                console.log('Error ' + err);
            });
        });
    },
    selectDB: function (event) {
        var self = this;

        var client = self.redisConnector.getConnection();
        client.select(event.detail, function () {

            self.redisCommands.fetchKeys(client, function (err, replies) {
                self.setKeys(replies);
            });
        });
    },
    setKeys: function (keys) {
        this.keys = this.redisCommands.toTree(keys);
        this.path = [];
        this.path.push(this.keys);
    },
	getKeys: function (o) {
	    return Object.keys(o);
	},
    openWord: function (event, detail, sender) {
        var word = sender.getAttribute('data-word'),
            col = parseInt(sender.getAttribute('data-col'), 10),
            nextPath;

        this.path = this.path.slice(0, col + 1);

        if (this.path[this.path.length - 1] && this.path[this.path.length - 1][word]) {
            nextPath = this.path[this.path.length - 1][word];

            if (Object.keys(nextPath).length) {
                this.path.push(nextPath);
            }
        }
    }
});<|endoftext|>


<commit_before>//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
//>>description: Behavior for "fixed" headers and footers
//>>label: Toolbars: Fixed
//>>group: Widgets
//>>css.structure: ../css/structure/jquery.mobile.fixedToolbar.css
//>>css.theme: ../css/themes/default/jquery.mobile.theme.css

define( [ "jquery", "../jquery.mobile.widget", "../jquery.mobile.core", "../jquery.mobile.navigation", "./page", "./page.sections", "../jquery.mobile.zoom" ], function( $ ) {
//>>excludeEnd("jqmBuildExclude");
(function( $, undefined ) {

	$.widget( "mobile.fixedtoolbar", $.mobile.widget, {
		options: {
			visibleOnPageShow: true,
			disablePageZoom: true,
			transition: "slide", //can be none, fade, slide (slide maps to slideup or slidedown)
			fullscreen: false,
			tapToggle: true,
			tapToggleBlacklist: "a, button, input, select, textarea, .ui-header-fixed, .ui-footer-fixed",
			hideDuringFocus: "input, textarea, select",
			updatePagePadding: true,
			trackPersistentToolbars: true,

			// Browser detection! Weeee, here we go...
			// Unfortunately, position:fixed is costly, not to mention probably impossible, to feature-detect accurately.
			// Some tests exist, but they currently return false results in critical devices and browsers, which could lead to a broken experience.
			// Testing fixed positioning is also pretty obtrusive to page load, requiring injected elements and scrolling the window
			// The following function serves to rule out some popular browsers with known fixed-positioning issues
			// This is a plugin option like any other, so feel free to improve or overwrite it
			supportBlacklist: function() {
				var w = window,
					ua = navigator.userAgent,
					platform = navigator.platform,
					// Rendering engine is Webkit, and capture major version
					wkmatch = ua.match( /AppleWebKit\/([0-9]+)/ ),
					wkversion = !!wkmatch && wkmatch[ 1 ],
					ffmatch = ua.match( /Fennec\/([0-9]+)/ ),
					ffversion = !!ffmatch && ffmatch[ 1 ],
					operammobilematch = ua.match( /Opera Mobi\/([0-9]+)/ ),
					omversion = !!operammobilematch && operammobilematch[ 1 ];

				if(
					// iOS 4.3 and older : Platform is iPhone/Pad/Touch and Webkit version is less than 534 (ios5)
					( ( platform.indexOf( "iPhone" ) > -1 || platform.indexOf( "iPad" ) > -1  || platform.indexOf( "iPod" ) > -1 ) && wkversion && wkversion < 534 ) ||
					// Opera Mini
					( w.operamini && ({}).toString.call( w.operamini ) === "[object OperaMini]" ) ||
					( operammobilematch && omversion < 7458 )	||
					//Android lte 2.1: Platform is Android and Webkit version is less than 533 (Android 2.2)
					( ua.indexOf( "Android" ) > -1 && wkversion && wkversion < 533 ) ||
					// Firefox Mobile before 6.0 -
					( ffversion && ffversion < 6 ) ||
					// WebOS less than 3
					( "palmGetResource" in window && wkversion && wkversion < 534 )	||
					// MeeGo
					( ua.indexOf( "MeeGo" ) > -1 && ua.indexOf( "NokiaBrowser/8.5.0" ) > -1 ) ) {
					return true;
				}

				return false;
			},
			initSelector: ":jqmData(position='fixed')"
		},

		_create: function() {

			var self = this,
				o = self.options,
				$el = self.element,
				tbtype = $el.is( ":jqmData(role='header')" ) ? "header" : "footer",
				$page = $el.closest( ".ui-page" );

			// Feature detecting support for
			if ( o.supportBlacklist() ) {
				self.destroy();
				return;
			}

			$el.addClass( "ui-"+ tbtype +"-fixed" );

			// "fullscreen" overlay positioning
			if ( o.fullscreen ) {
				$el.addClass( "ui-"+ tbtype +"-fullscreen" );
				$page.addClass( "ui-page-" + tbtype + "-fullscreen" );
			}
			// If not fullscreen, add class to page to set top or bottom padding
			else{
				$page.addClass( "ui-page-" + tbtype + "-fixed" );
			}

			self._addTransitionClass();
			self._bindPageEvents();
			self._bindToggleHandlers();
		},

		_addTransitionClass: function() {
			var tclass = this.options.transition;

			if ( tclass && tclass !== "none" ) {
				// use appropriate slide for header or footer
				if ( tclass === "slide" ) {
					tclass = this.element.is( ".ui-header" ) ? "slidedown" : "slideup";
				}

				this.element.addClass( tclass );
			}
		},

		_bindPageEvents: function() {
			var self = this,
				o = self.options,
				$el = self.element;

			//page event bindings
			// Fixed toolbars require page zoom to be disabled, otherwise usability issues crop up
			// This method is meant to disable zoom while a fixed-positioned toolbar page is visible
			$el.closest( ".ui-page" )
				.bind( "pagebeforeshow", function() {
					if ( o.disablePageZoom ) {
						$.mobile.zoom.disable( true );
					}
					if ( !o.visibleOnPageShow ) {
						self.hide( true );
					}
				} )
				.bind( "webkitAnimationStart animationstart updatelayout", function() {
					var thisPage = this;
					if ( o.updatePagePadding ) {
						self.updatePagePadding( thisPage );
					}
				})
				.bind( "pageshow", function() {
					var thisPage = this;
					self.updatePagePadding( thisPage );
					if ( o.updatePagePadding ) {
						$( window ).bind( "throttledresize." + self.widgetName, function() {
							self.updatePagePadding( thisPage );
						});
					}
				})
				.bind( "pagebeforehide", function( e, ui ) {
					if ( o.disablePageZoom ) {
						$.mobile.zoom.enable( true );
					}
					if ( o.updatePagePadding ) {
						$( window ).unbind( "throttledresize." + self.widgetName );
					}

					if ( o.trackPersistentToolbars ) {
						var thisFooter = $( ".ui-footer-fixed:jqmData(id)", this ),
							thisHeader = $( ".ui-header-fixed:jqmData(id)", this ),
							nextFooter = thisFooter.length && ui.nextPage && $( ".ui-footer-fixed:jqmData(id='" + thisFooter.jqmData( "id" ) + "')", ui.nextPage ) || $(),
							nextHeader = thisHeader.length && ui.nextPage && $( ".ui-header-fixed:jqmData(id='" + thisHeader.jqmData( "id" ) + "')", ui.nextPage ) || $();

							if ( nextFooter.length || nextHeader.length ) {

								nextFooter.add( nextHeader ).appendTo( $.mobile.pageContainer );

								ui.nextPage.one( "pageshow", function() {
									nextFooter.add( nextHeader ).appendTo( this );
								});
							}
					}
				});
		},

		_visible: true,

		// This will set the content element's top or bottom padding equal to the toolbar's height
		updatePagePadding: function( tbPage ) {
			var $el = this.element,
				header = $el.is( ".ui-header" );

			// This behavior only applies to "fixed", not "fullscreen"
			if ( this.options.fullscreen ) { return; }

			tbPage = tbPage || $el.closest( ".ui-page" );
			$( tbPage ).css( "padding-" + ( header ? "top" : "bottom" ), $el.outerHeight() );
		},

		_useTransition: function( notransition ) {
			var $win = $( window ),
				$el = this.element,
				scroll = $win.scrollTop(),
				elHeight = $el.height(),
				pHeight = $el.closest( ".ui-page" ).height(),
				viewportHeight = $.mobile.getScreenHeight(),
				tbtype = $el.is( ":jqmData(role='header')" ) ? "header" : "footer";

			return !notransition &&
				( this.options.transition && this.options.transition !== "none" &&
				(
					( tbtype === "header" && !this.options.fullscreen && scroll > elHeight ) ||
					( tbtype === "footer" && !this.options.fullscreen && scroll + viewportHeight < pHeight - elHeight )
				) || this.options.fullscreen
				);
		},

		show: function( notransition ) {
			var hideClass = "ui-fixed-hidden",
				$el = this.element;

			if ( this._useTransition( notransition ) ) {
				$el
					.removeClass( "out " + hideClass )
					.addClass( "in" );
			}
			else {
				$el.removeClass( hideClass );
			}
			this._visible = true;
		},

		hide: function( notransition ) {
			var hideClass = "ui-fixed-hidden",
				$el = this.element,
				// if it's a slide transition, our new transitions need the reverse class as well to slide outward
				outclass = "out" + ( this.options.transition === "slide" ? " reverse" : "" );

			if( this._useTransition( notransition ) ) {
				$el
					.addClass( outclass )
					.removeClass( "in" )
					.animationComplete(function() {
						$el.addClass( hideClass ).removeClass( outclass );
					});
			}
			else {
				$el.addClass( hideClass ).removeClass( outclass );
			}
			this._visible = false;
		},

		toggle: function() {
			this[ this._visible ? "hide" : "show" ]();
		},

		_bindToggleHandlers: function() {
			var self = this,
				o = self.options,
				$el = self.element;

			// tap toggle
			$el.closest( ".ui-page" )
				.bind( "vclick", function( e ) {
					if ( o.tapToggle && !$( e.target ).closest( o.tapToggleBlacklist ).length ) {
						self.toggle();
					}
				})
				.bind( "focusin focusout", function( e ) {
					if ( screen.width < 500 && $( e.target ).is( o.hideDuringFocus ) && !$( e.target ).closest( ".ui-header-fixed, .ui-footer-fixed" ).length ) {
						self[ ( e.type === "focusin" && self._visible ) ? "hide" : "show" ]();
					}
				});
		},

		destroy: function() {
			this.element.removeClass( "ui-header-fixed ui-footer-fixed ui-header-fullscreen ui-footer-fullscreen in out fade slidedown slideup ui-fixed-hidden" );
			this.element.closest( ".ui-page" ).removeClass( "ui-page-header-fixed ui-page-footer-fixed ui-page-header-fullscreen ui-page-footer-fullscreen" );
		}

	});

	//auto self-init widgets
	$( document )
		.bind( "pagecreate create", function( e ) {

			// DEPRECATED in 1.1: support for data-fullscreen=true|false on the page element.
			// This line ensures it still works, but we recommend moving the attribute to the toolbars themselves.
			if ( $( e.target ).jqmData( "fullscreen" ) ) {
				$( $.mobile.fixedtoolbar.prototype.options.initSelector, e.target ).not( ":jqmData(fullscreen)" ).jqmData( "fullscreen", true );
			}

			$.mobile.fixedtoolbar.prototype.enhanceWithin( e.target );
		});

})( jQuery );
//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
});
//>>excludeEnd("jqmBuildExclude");
<commit_msg>Prevents 1px shift of content on tapToggle.<commit_after>//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
//>>description: Behavior for "fixed" headers and footers
//>>label: Toolbars: Fixed
//>>group: Widgets
//>>css.structure: ../css/structure/jquery.mobile.fixedToolbar.css
//>>css.theme: ../css/themes/default/jquery.mobile.theme.css

define( [ "jquery", "../jquery.mobile.widget", "../jquery.mobile.core", "../jquery.mobile.navigation", "./page", "./page.sections", "../jquery.mobile.zoom" ], function( $ ) {
//>>excludeEnd("jqmBuildExclude");
(function( $, undefined ) {

	$.widget( "mobile.fixedtoolbar", $.mobile.widget, {
		options: {
			visibleOnPageShow: true,
			disablePageZoom: true,
			transition: "slide", //can be none, fade, slide (slide maps to slideup or slidedown)
			fullscreen: false,
			tapToggle: true,
			tapToggleBlacklist: "a, button, input, select, textarea, .ui-header-fixed, .ui-footer-fixed",
			hideDuringFocus: "input, textarea, select",
			updatePagePadding: true,
			trackPersistentToolbars: true,

			// Browser detection! Weeee, here we go...
			// Unfortunately, position:fixed is costly, not to mention probably impossible, to feature-detect accurately.
			// Some tests exist, but they currently return false results in critical devices and browsers, which could lead to a broken experience.
			// Testing fixed positioning is also pretty obtrusive to page load, requiring injected elements and scrolling the window
			// The following function serves to rule out some popular browsers with known fixed-positioning issues
			// This is a plugin option like any other, so feel free to improve or overwrite it
			supportBlacklist: function() {
				var w = window,
					ua = navigator.userAgent,
					platform = navigator.platform,
					// Rendering engine is Webkit, and capture major version
					wkmatch = ua.match( /AppleWebKit\/([0-9]+)/ ),
					wkversion = !!wkmatch && wkmatch[ 1 ],
					ffmatch = ua.match( /Fennec\/([0-9]+)/ ),
					ffversion = !!ffmatch && ffmatch[ 1 ],
					operammobilematch = ua.match( /Opera Mobi\/([0-9]+)/ ),
					omversion = !!operammobilematch && operammobilematch[ 1 ];

				if(
					// iOS 4.3 and older : Platform is iPhone/Pad/Touch and Webkit version is less than 534 (ios5)
					( ( platform.indexOf( "iPhone" ) > -1 || platform.indexOf( "iPad" ) > -1  || platform.indexOf( "iPod" ) > -1 ) && wkversion && wkversion < 534 ) ||
					// Opera Mini
					( w.operamini && ({}).toString.call( w.operamini ) === "[object OperaMini]" ) ||
					( operammobilematch && omversion < 7458 )	||
					//Android lte 2.1: Platform is Android and Webkit version is less than 533 (Android 2.2)
					( ua.indexOf( "Android" ) > -1 && wkversion && wkversion < 533 ) ||
					// Firefox Mobile before 6.0 -
					( ffversion && ffversion < 6 ) ||
					// WebOS less than 3
					( "palmGetResource" in window && wkversion && wkversion < 534 )	||
					// MeeGo
					( ua.indexOf( "MeeGo" ) > -1 && ua.indexOf( "NokiaBrowser/8.5.0" ) > -1 ) ) {
					return true;
				}

				return false;
			},
			initSelector: ":jqmData(position='fixed')"
		},

		_create: function() {

			var self = this,
				o = self.options,
				$el = self.element,
				tbtype = $el.is( ":jqmData(role='header')" ) ? "header" : "footer",
				$page = $el.closest( ".ui-page" );

			// Feature detecting support for
			if ( o.supportBlacklist() ) {
				self.destroy();
				return;
			}

			$el.addClass( "ui-"+ tbtype +"-fixed" );

			// "fullscreen" overlay positioning
			if ( o.fullscreen ) {
				$el.addClass( "ui-"+ tbtype +"-fullscreen" );
				$page.addClass( "ui-page-" + tbtype + "-fullscreen" );
			}
			// If not fullscreen, add class to page to set top or bottom padding
			else{
				$page.addClass( "ui-page-" + tbtype + "-fixed" );
			}

			self._addTransitionClass();
			self._bindPageEvents();
			self._bindToggleHandlers();
		},

		_addTransitionClass: function() {
			var tclass = this.options.transition;

			if ( tclass && tclass !== "none" ) {
				// use appropriate slide for header or footer
				if ( tclass === "slide" ) {
					tclass = this.element.is( ".ui-header" ) ? "slidedown" : "slideup";
				}

				this.element.addClass( tclass );
			}
		},

		_bindPageEvents: function() {
			var self = this,
				o = self.options,
				$el = self.element;

			//page event bindings
			// Fixed toolbars require page zoom to be disabled, otherwise usability issues crop up
			// This method is meant to disable zoom while a fixed-positioned toolbar page is visible
			$el.closest( ".ui-page" )
				.bind( "pagebeforeshow", function() {
					if ( o.disablePageZoom ) {
						$.mobile.zoom.disable( true );
					}
					if ( !o.visibleOnPageShow ) {
						self.hide( true );
					}
				} )
				.bind( "webkitAnimationStart animationstart updatelayout", function() {
					var thisPage = this;
					if ( o.updatePagePadding ) {
						self.updatePagePadding( thisPage );
					}
				})
				.bind( "pageshow", function() {
					var thisPage = this;
					self.updatePagePadding( thisPage );
					if ( o.updatePagePadding ) {
						$( window ).bind( "throttledresize." + self.widgetName, function() {
							self.updatePagePadding( thisPage );
						});
					}
				})
				.bind( "pagebeforehide", function( e, ui ) {
					if ( o.disablePageZoom ) {
						$.mobile.zoom.enable( true );
					}
					if ( o.updatePagePadding ) {
						$( window ).unbind( "throttledresize." + self.widgetName );
					}

					if ( o.trackPersistentToolbars ) {
						var thisFooter = $( ".ui-footer-fixed:jqmData(id)", this ),
							thisHeader = $( ".ui-header-fixed:jqmData(id)", this ),
							nextFooter = thisFooter.length && ui.nextPage && $( ".ui-footer-fixed:jqmData(id='" + thisFooter.jqmData( "id" ) + "')", ui.nextPage ) || $(),
							nextHeader = thisHeader.length && ui.nextPage && $( ".ui-header-fixed:jqmData(id='" + thisHeader.jqmData( "id" ) + "')", ui.nextPage ) || $();

							if ( nextFooter.length || nextHeader.length ) {

								nextFooter.add( nextHeader ).appendTo( $.mobile.pageContainer );

								ui.nextPage.one( "pageshow", function() {
									nextFooter.add( nextHeader ).appendTo( this );
								});
							}
					}
				});
		},

		_visible: true,

		// This will set the content element's top or bottom padding equal to the toolbar's height
		updatePagePadding: function( tbPage ) {
			var $el = this.element,
				header = $el.is( ".ui-header" ),
				pos = parseFloat( $el.css( header ? "top" : "bottom" ) );

			// This behavior only applies to "fixed", not "fullscreen"
			if ( this.options.fullscreen ) { return; }

			tbPage = tbPage || $el.closest( ".ui-page" );
			$( tbPage ).css( "padding-" + ( header ? "top" : "bottom" ), $el.outerHeight() + pos );
		},

		_useTransition: function( notransition ) {
			var $win = $( window ),
				$el = this.element,
				scroll = $win.scrollTop(),
				elHeight = $el.height(),
				pHeight = $el.closest( ".ui-page" ).height(),
				viewportHeight = $.mobile.getScreenHeight(),
				tbtype = $el.is( ":jqmData(role='header')" ) ? "header" : "footer";

			return !notransition &&
				( this.options.transition && this.options.transition !== "none" &&
				(
					( tbtype === "header" && !this.options.fullscreen && scroll > elHeight ) ||
					( tbtype === "footer" && !this.options.fullscreen && scroll + viewportHeight < pHeight - elHeight )
				) || this.options.fullscreen
				);
		},

		show: function( notransition ) {
			var hideClass = "ui-fixed-hidden",
				$el = this.element;

			if ( this._useTransition( notransition ) ) {
				$el
					.removeClass( "out " + hideClass )
					.addClass( "in" );
			}
			else {
				$el.removeClass( hideClass );
			}
			this._visible = true;
		},

		hide: function( notransition ) {
			var hideClass = "ui-fixed-hidden",
				$el = this.element,
				// if it's a slide transition, our new transitions need the reverse class as well to slide outward
				outclass = "out" + ( this.options.transition === "slide" ? " reverse" : "" );

			if( this._useTransition( notransition ) ) {
				$el
					.addClass( outclass )
					.removeClass( "in" )
					.animationComplete(function() {
						$el.addClass( hideClass ).removeClass( outclass );
					});
			}
			else {
				$el.addClass( hideClass ).removeClass( outclass );
			}
			this._visible = false;
		},

		toggle: function() {
			this[ this._visible ? "hide" : "show" ]();
		},

		_bindToggleHandlers: function() {
			var self = this,
				o = self.options,
				$el = self.element;

			// tap toggle
			$el.closest( ".ui-page" )
				.bind( "vclick", function( e ) {
					if ( o.tapToggle && !$( e.target ).closest( o.tapToggleBlacklist ).length ) {
						self.toggle();
					}
				})
				.bind( "focusin focusout", function( e ) {
					if ( screen.width < 500 && $( e.target ).is( o.hideDuringFocus ) && !$( e.target ).closest( ".ui-header-fixed, .ui-footer-fixed" ).length ) {
						self[ ( e.type === "focusin" && self._visible ) ? "hide" : "show" ]();
					}
				});
		},

		destroy: function() {
			this.element.removeClass( "ui-header-fixed ui-footer-fixed ui-header-fullscreen ui-footer-fullscreen in out fade slidedown slideup ui-fixed-hidden" );
			this.element.closest( ".ui-page" ).removeClass( "ui-page-header-fixed ui-page-footer-fixed ui-page-header-fullscreen ui-page-footer-fullscreen" );
		}

	});

	//auto self-init widgets
	$( document )
		.bind( "pagecreate create", function( e ) {

			// DEPRECATED in 1.1: support for data-fullscreen=true|false on the page element.
			// This line ensures it still works, but we recommend moving the attribute to the toolbars themselves.
			if ( $( e.target ).jqmData( "fullscreen" ) ) {
				$( $.mobile.fixedtoolbar.prototype.options.initSelector, e.target ).not( ":jqmData(fullscreen)" ).jqmData( "fullscreen", true );
			}

			$.mobile.fixedtoolbar.prototype.enhanceWithin( e.target );
		});

})( jQuery );
//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
});
//>>excludeEnd("jqmBuildExclude");
<|endoftext|>


<commit_before>/* jshint camelcase:false */
define([
    'dojo/text!./resources/Address.html',

    'dojo/_base/declare',
    'dojo/Deferred',
    'dojo/aspect',

    'agrc/widgets/locate/FindAddress',

    'esri/tasks/GeometryService',
    'esri/tasks/BufferParameters',
    'esri/geometry/Point',

    '../config'
], function(
    template,

    declare,
    Deferred,
    aspect,

    FindAddress,

    GeometryService,
    BufferParameters,
    Point,

    config
) {
    return declare([FindAddress], {
        // description:
        //      Street address controls and logic.

        templateString: template,

        // geometryService: GeometryService
        geometryService: null,

        // getGeometryDef: Deferred
        getGeometryDef: null,

        postCreate: function () {
            // summary:
            //      description
            console.log('app/search/Address::postCreate', arguments);

            var that = this;

            aspect.after(this, 'onFind', this.buffer, true);
            aspect.after(this, '_onError', function () {
                that.getGeometryDef.reject('There was an error with the find address service.');
            });

            this.inherited(arguments);
        },
        getGeometry: function () {
            // summary:
            //      called by Search
            // returns: dojo/promise/Promise
            //      Promise is resolved with a esri/geometry/Polygon object
            console.log('app/search/Address::getGeometry', arguments);

            this.getGeometryDef = new Deferred();

            // have to validate manually to incorporate buffer but also because
            // FindAddress:geocodeAddress is written in a way that I can't
            // tell if it validates or not.
            if (!this._validate() || !this._isValid(this.numBuffer)) {
                this.getGeometryDef.reject();
            }

            // kick off geocoding - buffer is called on successful match
            this.geocodeAddress();

            return this.getGeometryDef.promise;
        },
        buffer: function (geocodeResult) {
            // summary:
            //      Called after a successful geocode
            // geocodeResult: Object
            //      {
            //          location: {
            //              x: Number,
            //              y: Number
            //          }
            //      }
            console.log('module.id::buffer', arguments);

            var that = this;

            if (!this.geometryService) {
                this.geometryService = new GeometryService(config.urls.geometryService);
                this.geometryService.on('buffer-complete', function (result) {
                    that.getGeometryDef.resolve(result.geometries[0]);
                });
                this.geometryService.on('error', function () {
                    that.getGeometryDef.reject('There was an error with the buffer.');
                });
            }

            var params = new BufferParameters();

            params.distances = [this.numBuffer.value];
            var point = new Point(
                geocodeResult.location.x,
                geocodeResult.location.y,
                config.spatialReference
            );
            params.geometries = [point];
            params.spatialReference = config.spatialReference;
            params.unit = GeometryService.UNIT_MILE;

            this.geometryService.buffer(params);
        }
    });
});<commit_msg>Zoom to and show buffer area in Address search. Fixed bug with units.<commit_after>/* jshint camelcase:false */
define([
    'dojo/text!./resources/Address.html',

    'dojo/_base/declare',
    'dojo/Deferred',
    'dojo/aspect',
    'dojo/topic',

    'agrc/widgets/locate/FindAddress',

    'esri/tasks/GeometryService',
    'esri/tasks/BufferParameters',
    'esri/geometry/Point',

    '../config'
], function(
    template,

    declare,
    Deferred,
    aspect,
    topic,

    FindAddress,

    GeometryService,
    BufferParameters,
    Point,

    config
) {
    return declare([FindAddress], {
        // description:
        //      Street address controls and logic.

        templateString: template,

        // geometryService: GeometryService
        geometryService: null,

        // getGeometryDef: Deferred
        getGeometryDef: null,

        postCreate: function () {
            // summary:
            //      description
            console.log('app/search/Address::postCreate', arguments);

            var that = this;

            aspect.after(this, 'onFind', this.buffer, true);
            aspect.after(this, '_onError', function () {
                that.getGeometryDef.reject('There was an error with the find address service.');
            });

            this.inherited(arguments);
        },
        getGeometry: function () {
            // summary:
            //      called by Search
            // returns: dojo/promise/Promise
            //      Promise is resolved with a esri/geometry/Polygon object
            console.log('app/search/Address::getGeometry', arguments);

            this.getGeometryDef = new Deferred();

            // have to validate manually to incorporate buffer but also because
            // FindAddress:geocodeAddress is written in a way that I can't
            // tell if it validates or not.
            if (!this._validate() || !this._isValid(this.numBuffer)) {
                this.getGeometryDef.reject();
            }

            // kick off geocoding - buffer is called on successful match
            this.geocodeAddress();

            return this.getGeometryDef.promise;
        },
        buffer: function (geocodeResult) {
            // summary:
            //      Called after a successful geocode
            // geocodeResult: Object
            //      {
            //          location: {
            //              x: Number,
            //              y: Number
            //          }
            //      }
            console.log('module.id::buffer', arguments);

            var that = this;

            if (!this.geometryService) {
                this.geometryService = new GeometryService(config.urls.geometryService);
                this.geometryService.on('buffer-complete', function (result) {
                    that.getGeometryDef.resolve(result.geometries[0]);
                    topic.publish(config.topics.mapController.zoomTo, result.geometries[0]);
                });
                this.geometryService.on('error', function () {
                    that.getGeometryDef.reject('There was an error with the buffer.');
                });
            }

            var params = new BufferParameters();

            params.distances = [this.numBuffer.value];
            var point = new Point(
                geocodeResult.location.x,
                geocodeResult.location.y,
                config.spatialReference
            );
            params.geometries = [point];
            params.spatialReference = config.spatialReference;
            params.unit = GeometryService.UNIT_US_NAUTICAL_MILE;

            this.geometryService.buffer(params);
        }
    });
});<|endoftext|>


<commit_before>/*
  mongresto 0.3.84... Changing things!

  May 2016 Nodebite AB, Thomas Frank

  MIT Licensed - use anywhere you want!

  A mongodb REST service for Node.js Express + mongoose
  working with mongoose models
  + generating Angular resource objects on the fly
*/

/*
  Nodebite code style -> jshint settings below, also
  indent = 2 spaces, keep your rows reasonably short
  also your methods below sceen height.
*/

var mongresto = (function(){ return {

  load:function(){
    var path = __dirname + '/mongresto-modules/', m;
    require('fs').readdirSync(path).forEach(function(x){
      m = require(path + x);
      this[m.name] = m;
    },this);
  }

};})();

// Start up
function pub(options){
  var m = {};
  [
    "path", "express", "body-parser", "app-root-path", "compression"
  ].forEach(function(x){ m[x.replace(/\W/g,'')] = require(x); });
  mongresto.load();
  var app = m.express();
  app.use(m.compression());
  app.use(m.bodyparser.json());
  app.use(m.bodyparser.urlencoded({ extended: false }));
  mongresto.init(app,options,m);
  return app;
}

// The old init method (deprecated but supported for a while)
pub.init = function(app,options){
  mongresto.load();
  return mongresto.init.apply(mongresto,[app,options]);
};

module.exports = pub;
<commit_msg><commit_after>/*
  mongresto 0.3.84

  May 2016 Nodebite AB, Thomas Frank

  MIT Licensed - use anywhere you want!

  A mongodb REST service for Node.js Express + mongoose
  working with mongoose models
  + generating Angular resource objects on the fly
*/

/*
  Nodebite code style -> jshint settings below, also
  indent = 2 spaces, keep your rows reasonably short
  also your methods below sceen height.
*/

var mongresto = (function(){ return {

  load:function(){
    var path = __dirname + '/mongresto-modules/', m;
    require('fs').readdirSync(path).forEach(function(x){
      m = require(path + x);
      this[m.name] = m;
    },this);
  }

};})();

// Start up
function pub(options){
  var m = {};
  [
    "path", "express", "body-parser", "app-root-path", "compression"
  ].forEach(function(x){ m[x.replace(/\W/g,'')] = require(x); });
  mongresto.load();
  var app = m.express();
  app.use(m.compression());
  app.use(m.bodyparser.json());
  app.use(m.bodyparser.urlencoded({ extended: false }));
  mongresto.init(app,options,m);
  return app;
}

// The old init method (deprecated but supported for a while)
pub.init = function(app,options){
  mongresto.load();
  return mongresto.init.apply(mongresto,[app,options]);
};

module.exports = pub;
<|endoftext|>


<commit_before>Handlebars.registerHelper('select_box', function(field) {
  var html_options, options,
  _this = this;
  if (!field) {
    return;
  }
  options = "" + field + "Options";
  html_options = [];
  _.each(this[options](), function(option) {
    var selected;
    selected = _this[field] === option ? ' selected' : '';
    return html_options.push("<option" + selected + ">" + option + "</option>");
  });
  html = "<select class='form-control' name='" + field + "'>" + (html_options.join('')) + "</select>"
  return new Handlebars.SafeString(html);
});

Handlebars.registerHelper('check_box', function(field) {
  var capitalizedField, checked;
  if (!field) {
    return;
  }
  capitalizedField = field.charAt(0).toUpperCase() + field.slice(1);
  checked = this[field] === 'true' ? ' checked' : '';
  html = "<input name='" + field + "' type='hidden' value='false'><input name='" + field + "' type='checkbox' value='true' " + checked + ">" + capitalizedField + "";
  return new Handlebars.SafeString(html);
});
<commit_msg>Humanize the options to be more readable on select boxes.<commit_after>Handlebars.registerHelper('select_box', function(field) {
  var html_options, options,
  _this = this;
  if (!field) {
    return;
  }
  options = "" + field + "Options";
  html_options = [];
  _.each(this[options](), function(option) {
    var selected;
    selected = _this[field] === option ? ' selected' : '';
    return html_options.push("<option value='" + option + "'" + selected + ">" + _.humanize(option) + "</option>");
  });
  html = "<select class='form-control' name='" + field + "'>" + (html_options.join('')) + "</select>"
  return new Handlebars.SafeString(html);
});

Handlebars.registerHelper('check_box', function(field) {
  var capitalizedField, checked;
  if (!field) {
    return;
  }
  capitalizedField = field.charAt(0).toUpperCase() + field.slice(1);
  checked = this[field] === 'true' ? ' checked' : '';
  html = "<input name='" + field + "' type='hidden' value='false'><input name='" + field + "' type='checkbox' value='true' " + checked + ">" + capitalizedField + "";
  return new Handlebars.SafeString(html);
});
<|endoftext|>


<commit_before>/*
 @author Zakai Hamilton
 @component AppWorkshop
 */

screens.app.workshop = function AppWorkshop(me, { core, ui, widget, db, lib, react }) {
    const {
        DropDown,
        Item,
        Bar,
        Field,
        Element,
        Direction,
        Language,
        Text,
        Input,
        Clone,
        Separator
    } = react;

    const AppToolbar = ({ meetingState, languageState, filterState, sortState, searchState, sortDirectionState }) => {
        const languageItems = (me.languages || []).map(language => {
            const name = core.string.title(language.name);
            return (<Item key={language.id} id={language.id}>{name}</Item>);
        });
        const sortItems = (me.sort || []).map(sort => {
            return (<Item key={sort.id} id={sort.id}>{sort.name}</Item>);
        });
        const sortDirectionItems = (me.sortDirection || []).map(sort => {
            return (<Item key={sort.id} id={sort.id}>{sort.name}</Item>);
        });
        const filterItems = (me.filters || []).map(filter => {
            return (<Item key={filter.id} id={filter.id}>{filter.name}</Item>);
        });
        const meetingItems = (me.meetings || []).map(meeting => {
            return (<Item key={meeting.name} id={meeting.id}>{meeting.name.split(" ")[0]}</Item>);
        });
        const noneItem = (<Item id="none" multiple={false}>
            <Text language="eng">None</Text>
            <Text language="heb">אין</Text>
        </Item>);
        return (
            <Bar>
                <Field label={
                    <>
                        <Text language="eng">Language</Text>
                        <Text language="heb">שפה</Text>
                    </>
                }>
                    <DropDown state={languageState}>
                        {languageItems}
                    </DropDown>
                </Field>
                <Field label={
                    <>
                        <Text language="eng">Sort</Text>
                        <Text language="heb">מיון</Text>
                    </>
                }>
                    <DropDown state={sortState}>
                        {sortItems}
                        <Separator />
                        <Clone state={sortDirectionState} hideCurrent={true}>
                            {sortDirectionItems}
                        </Clone>
                    </DropDown>
                </Field>
                <Field label={
                    <>
                        <Text language="eng">Filter</Text>
                        <Text language="heb">סינון</Text>
                    </>
                }>
                    <DropDown state={filterState}>
                        {noneItem}
                        {filterItems}
                    </DropDown>
                </Field>
                <Field label={
                    <>
                        <Text language="eng">Meeting</Text>
                        <Text language="heb">פגישה</Text>
                    </>
                }>
                    <DropDown state={meetingState}>
                        <Item id={0} hideInList={true}>
                            <Text language="eng">None</Text>
                            <Text language="heb">בלי</Text>
                        </Item>
                        {meetingItems}
                    </DropDown>
                </Field>
                <Element style={{ flex: 1 }}></Element>
                <Field label={
                    <>
                        <Text language="eng">Search</Text>
                        <Text language="heb">חיפוש</Text>
                    </>
                }>
                    <Input size="12" type="Search" state={searchState} />
                </Field>
            </Bar>
        );
    };

    const User = ({ user_name, count, active, select, index }) => {
        const disabled = count <= 0;
        if (disabled) {
            select = null;
        }
        return (<Element className={{ "app-workshop-user": true, active: active && !disabled, disabled }} onClick={select}>
            <Element title={user_name} className="app-workshop-user-long-name">{user_name}</Element>
            <Element className="app-workshop-user-short-name">{user_name.split(/[ \@]/)[0]}</Element>
            <Element className="app-workshop-user-index">{index}</Element>
        </Element>);
    };

    const AppHub = ({ meetingState, currentUserState, filterState, sortState, searchState, sortDirectionState, updateState }) => {
        const [meeting] = meetingState;
        const [currentUserId, setCurrentUserId] = currentUserState;
        const [sort] = sortState;
        const [filter] = filterState;
        const [search] = searchState;
        const [direction] = sortDirectionState;
        const [counter] = updateState;
        react.util.useSubscribe(meetingState);
        react.util.useSubscribe(currentUserState);
        react.util.useSubscribe(sortState);
        react.util.useSubscribe(updateState);
        react.util.useSubscribe(filterState);
        react.util.useSubscribe(searchState);
        const users = react.util.useData(() => {
            if (!meeting) {
                return;
            }
            let { users } = me.meetings.find(item => item.id === meeting);
            me.filters.forEach(filterItem => {
                if (filter.includes(filterItem.id)) {
                    users = filterItem.filter(users);
                }
            });
            users = me.sort.find(item => item.id === sort).sort(users, meeting);
            if (search) {
                users = users.filter(user => user.user_name.toLowerCase().includes(search.toLowerCase()));
            }
            if (direction === "asc") {
                users = users.reverse();
            }
            return users.map((user, index) => {
                const selectUser = () => {
                    setCurrentUserId(currentUserId !== user.user_id && user.user_id);
                }
                return (
                    <User key={user.user_id} {...user} index={index} active={currentUserId === user.user_id} select={selectUser} />
                );
            });
        }, [currentUserId, counter, meeting, sort, filter, direction, search]);
        return (<Element className="app-workshop-users">
            {users}
        </Element>);
    };

    const Main = () => {
        const firstMeetingId = me.meetings && me.meetings.length && me.meetings[0].id;
        const [isOpen, setOpen] = react.util.useState(true);
        const delayState = react.util.useState(5000);
        const meetingState = react.util.useState(firstMeetingId);
        const currentUserState = react.util.useState(0);
        const languageState = react.util.useState("eng");
        const sortState = react.util.useState("date");
        const filterState = react.util.useState(["meeting"]);
        const sortDirectionState = react.util.useState("desc");
        const updateState = react.util.useState(0);
        const searchState = react.util.useState("");
        const [meetingId, setMeetingId] = meetingState;
        const [language] = languageState;
        const [delay] = delayState;
        const direction = me.languages.find(item => item.id === language).direction;
        React.useEffect(() => {
            if (me.timerHandle) {
                clearInterval(me.timerHandle);
                me.timerHandle = null;
            }
            if (isOpen && delay) {
                me.timerHandle = setInterval(me.loadMeetings, delay);
            }
            me.loadMeetings();
            if (!meetingId) {
                setMeetingId(firstMeetingId);
            }
            return () => {
                if (me.timerHandle) {
                    clearInterval(me.timerHandle);
                    me.timerHandle = null;
                }
            };
        }, [delay, isOpen]);
        React.useEffect(() => {
            if (!meetingId) {
                setMeetingId(firstMeetingId);
            }
        }, [firstMeetingId]);
        me.redraw = () => {
            const [counter, setCounter] = updateState;
            if (!meetingId) {
                setMeetingId(firstMeetingId);
            }
            setCounter(counter + 1);
        };
        me.close = () => {
            me.singleton = null;
            setOpen(false);
        };
        const state = {
            meetingState,
            currentUserState,
            languageState,
            sortState,
            filterState,
            sortDirectionState,
            updateState,
            delayState,
            searchState
        };
        if (!isOpen) {
            return null;
        }
        return (
            <Direction direction={direction}>
                <Language language={language}>
                    <AppToolbar {...state} />
                    <AppHub {...state} />
                </Language>
            </Direction>
        );
    };
    me.meetings = [];
    me.languages = [
        {
            id: "eng",
            name: "English",
            direction: "ltr",
            locale: "en-US"
        },
        {
            id: "heb",
            name: "עברית",
            direction: "rtl",
            locale: "he-IL"
        }
    ];
    me.sortDirection = [
        {
            id: "asc",
            name: (
                <>
                    <Text language="eng">Ascending</Text>
                    <Text language="heb">עולה</Text>
                </>
            )
        },
        {
            id: "desc",
            name: (
                <>
                    <Text language="eng">Descending</Text>
                    <Text language="heb">יורד</Text>
                </>
            )
        }
    ];
    me.sort = [
        {
            id: "date",
            name: (
                <>
                    <Text language="eng">Date</Text>
                    <Text language="heb">תאריך</Text>
                </>
            ),
            sort: (items) => {
                items = [...items];
                items.sort((a, b) => {
                    return b.index - a.index;
                });
                return items;
            }
        },
        {
            id: "name",
            name: (
                <>
                    <Text language="eng">Name</Text>
                    <Text language="heb">שם</Text>
                </>
            ),
            sort: (items) => {
                items = [...items];
                items.sort((a, b) => {
                    return a.user_name.localeCompare(b.user_name);
                });
                return items;
            }
        },
        {
            id: "shuffle",
            name: (
                <>
                    <Text language="eng">Shuffle</Text>
                    <Text language="heb">ערבוב</Text>
                </>
            ),
            sort: (items, meeting) => {
                items = [...items];
                const meetingHash = parseInt(core.string.hash(meeting));
                let hashId = function (userId) {
                    const idHash = parseInt(core.string.hash(userId));
                    return meetingHash * idHash;
                };
                items.sort((a, b) => {
                    return hashId(a.user_id) - hashId(b.user_id);
                });
                return items;
            }
        }
    ];
    me.filters = [
        {
            id: "online",
            name: (
                <>
                    <Text language="eng">Online</Text>
                    <Text language="heb">אונליין</Text>
                </>
            ),
            filter: (items) => {
                items = items.filter(item => item.count > 0);
                return items;
            }
        },
        {
            id: "meeting",
            name: (
                <>
                    <Text language="eng">Meeting</Text>
                    <Text language="heb">פגישה</Text>
                </>
            ),
            filter: (items) => {
                items = items.filter(item => item.current);
                return items;
            }
        },
        {
            id: "speak",
            name: (
                <>
                    <Text language="eng">Can Speak</Text>
                    <Text language="heb">יכול לדבר</Text>
                </>
            ),
            filter: (items) => {
                items = items.filter(item => {
                    return !(["listening", "work"].find(term => item.user_name.toLowerCase().includes(term)));
                });
                return items;
            }
        }
    ];
    me.launch = async function () {
        if (core.property.get(me.singleton, "ui.node.parent")) {
            core.property.set(me.singleton, "widget.window.show", true);
            return me.singleton;
        }
        me.singleton = ui.element.create(me.json, "workspace", "self");
    };
    me.loadMeetings = async function () {
        me.meetings = await lib.zoom.meetings();
        if (me.redraw) {
            me.redraw();
        }
    };
    me.initOptions = async function (object) {
        var window = widget.window.get(object);
        ui.options.load(me, window, {
            room: ""
        });
        core.property.set(window, "app", me);
        core.property.set(window, "name", "");
    };
    me.visibilityChange = function () {
        const visibilityState = ui.session.visibilityState();
        if (me.singleton && visibilityState === "visible") {
            me.loadMeetings();
        }
    };
    me.render = function (object) {
        return (<Main />);
    };
    me.resize = function (object) {

    };
};
<commit_msg>bug fix<commit_after>/*
 @author Zakai Hamilton
 @component AppWorkshop
 */

screens.app.workshop = function AppWorkshop(me, { core, ui, widget, db, lib, react }) {
    const {
        DropDown,
        Item,
        Bar,
        Field,
        Element,
        Direction,
        Language,
        Text,
        Input,
        Clone,
        Separator
    } = react;

    const AppToolbar = ({ meetingState, languageState, filterState, sortState, searchState, sortDirectionState }) => {
        const languageItems = (me.languages || []).map(language => {
            const name = core.string.title(language.name);
            return (<Item key={language.id} id={language.id}>{name}</Item>);
        });
        const sortItems = (me.sort || []).map(sort => {
            return (<Item key={sort.id} id={sort.id}>{sort.name}</Item>);
        });
        const sortDirectionItems = (me.sortDirection || []).map(sort => {
            return (<Item key={sort.id} id={sort.id}>{sort.name}</Item>);
        });
        const filterItems = (me.filters || []).map(filter => {
            return (<Item key={filter.id} id={filter.id}>{filter.name}</Item>);
        });
        const meetingItems = (me.meetings || []).map(meeting => {
            return (<Item key={meeting.name} id={meeting.id}>{meeting.name.split(" ")[0]}</Item>);
        });
        const noneItem = (<Item id="none" multiple={false}>
            <Text language="eng">None</Text>
            <Text language="heb">אין</Text>
        </Item>);
        return (
            <Bar>
                <Field label={
                    <>
                        <Text language="eng">Language</Text>
                        <Text language="heb">שפה</Text>
                    </>
                }>
                    <DropDown state={languageState}>
                        {languageItems}
                    </DropDown>
                </Field>
                <Field label={
                    <>
                        <Text language="eng">Sort</Text>
                        <Text language="heb">מיון</Text>
                    </>
                }>
                    <DropDown state={sortState}>
                        {sortItems}
                        <Separator />
                        <Clone state={sortDirectionState} hideCurrent={true}>
                            {sortDirectionItems}
                        </Clone>
                    </DropDown>
                </Field>
                <Field label={
                    <>
                        <Text language="eng">Filter</Text>
                        <Text language="heb">סינון</Text>
                    </>
                }>
                    <DropDown state={filterState}>
                        {noneItem}
                        {filterItems}
                    </DropDown>
                </Field>
                <Field label={
                    <>
                        <Text language="eng">Meeting</Text>
                        <Text language="heb">פגישה</Text>
                    </>
                }>
                    <DropDown state={meetingState}>
                        <Item id={0} hideInList={true}>
                            <Text language="eng">None</Text>
                            <Text language="heb">בלי</Text>
                        </Item>
                        {meetingItems}
                    </DropDown>
                </Field>
                <Element style={{ flex: 1 }}></Element>
                <Field label={
                    <>
                        <Text language="eng">Search</Text>
                        <Text language="heb">חיפוש</Text>
                    </>
                }>
                    <Input size="12" type="Search" state={searchState} />
                </Field>
            </Bar>
        );
    };

    const User = ({ user_name, count, active, select, index }) => {
        const disabled = count <= 0;
        if (disabled) {
            select = null;
        }
        return (<Element className={{ "app-workshop-user": true, active: active && !disabled, disabled }} onClick={select}>
            <Element title={user_name} className="app-workshop-user-long-name">{user_name}</Element>
            <Element className="app-workshop-user-short-name">{user_name.split(/[ \@]/)[0]}</Element>
            <Element className="app-workshop-user-index">{index + 1}</Element>
        </Element>);
    };

    const AppHub = ({ meetingState, currentUserState, filterState, sortState, searchState, sortDirectionState, updateState }) => {
        const [meeting] = meetingState;
        const [currentUserId, setCurrentUserId] = currentUserState;
        const [sort] = sortState;
        const [filter] = filterState;
        const [search] = searchState;
        const [direction] = sortDirectionState;
        const [counter] = updateState;
        react.util.useSubscribe(meetingState);
        react.util.useSubscribe(currentUserState);
        react.util.useSubscribe(sortState);
        react.util.useSubscribe(updateState);
        react.util.useSubscribe(filterState);
        react.util.useSubscribe(searchState);
        const users = react.util.useData(() => {
            if (!meeting) {
                return;
            }
            let { users } = me.meetings.find(item => item.id === meeting);
            me.filters.forEach(filterItem => {
                if (filter.includes(filterItem.id)) {
                    users = filterItem.filter(users);
                }
            });
            users = me.sort.find(item => item.id === sort).sort(users, meeting);
            if (search) {
                users = users.filter(user => user.user_name.toLowerCase().includes(search.toLowerCase()));
            }
            if (direction === "asc") {
                users = users.reverse();
            }
            return users.map((user, index) => {
                const selectUser = () => {
                    setCurrentUserId(currentUserId !== user.user_id && user.user_id);
                }
                return (
                    <User key={user.user_id} {...user} index={index} active={currentUserId === user.user_id} select={selectUser} />
                );
            });
        }, [currentUserId, counter, meeting, sort, filter, direction, search]);
        return (<Element className="app-workshop-users">
            {users}
        </Element>);
    };

    const Main = () => {
        const firstMeetingId = me.meetings && me.meetings.length && me.meetings[0].id;
        const [isOpen, setOpen] = react.util.useState(true);
        const delayState = react.util.useState(5000);
        const meetingState = react.util.useState(firstMeetingId);
        const currentUserState = react.util.useState(0);
        const languageState = react.util.useState("eng");
        const sortState = react.util.useState("date");
        const filterState = react.util.useState(["meeting"]);
        const sortDirectionState = react.util.useState("desc");
        const updateState = react.util.useState(0);
        const searchState = react.util.useState("");
        const [meetingId, setMeetingId] = meetingState;
        const [language] = languageState;
        const [delay] = delayState;
        const direction = me.languages.find(item => item.id === language).direction;
        React.useEffect(() => {
            if (me.timerHandle) {
                clearInterval(me.timerHandle);
                me.timerHandle = null;
            }
            if (isOpen && delay) {
                me.timerHandle = setInterval(me.loadMeetings, delay);
            }
            me.loadMeetings();
            if (!meetingId) {
                setMeetingId(firstMeetingId);
            }
            return () => {
                if (me.timerHandle) {
                    clearInterval(me.timerHandle);
                    me.timerHandle = null;
                }
            };
        }, [delay, isOpen]);
        React.useEffect(() => {
            if (!meetingId) {
                setMeetingId(firstMeetingId);
            }
        }, [firstMeetingId]);
        me.redraw = () => {
            const [counter, setCounter] = updateState;
            if (!meetingId) {
                setMeetingId(firstMeetingId);
            }
            setCounter(counter + 1);
        };
        me.close = () => {
            me.singleton = null;
            setOpen(false);
        };
        const state = {
            meetingState,
            currentUserState,
            languageState,
            sortState,
            filterState,
            sortDirectionState,
            updateState,
            delayState,
            searchState
        };
        if (!isOpen) {
            return null;
        }
        return (
            <Direction direction={direction}>
                <Language language={language}>
                    <AppToolbar {...state} />
                    <AppHub {...state} />
                </Language>
            </Direction>
        );
    };
    me.meetings = [];
    me.languages = [
        {
            id: "eng",
            name: "English",
            direction: "ltr",
            locale: "en-US"
        },
        {
            id: "heb",
            name: "עברית",
            direction: "rtl",
            locale: "he-IL"
        }
    ];
    me.sortDirection = [
        {
            id: "asc",
            name: (
                <>
                    <Text language="eng">Ascending</Text>
                    <Text language="heb">עולה</Text>
                </>
            )
        },
        {
            id: "desc",
            name: (
                <>
                    <Text language="eng">Descending</Text>
                    <Text language="heb">יורד</Text>
                </>
            )
        }
    ];
    me.sort = [
        {
            id: "date",
            name: (
                <>
                    <Text language="eng">Date</Text>
                    <Text language="heb">תאריך</Text>
                </>
            ),
            sort: (items) => {
                items = [...items];
                items.sort((a, b) => {
                    return b.index - a.index;
                });
                return items;
            }
        },
        {
            id: "name",
            name: (
                <>
                    <Text language="eng">Name</Text>
                    <Text language="heb">שם</Text>
                </>
            ),
            sort: (items) => {
                items = [...items];
                items.sort((a, b) => {
                    return a.user_name.localeCompare(b.user_name);
                });
                return items;
            }
        },
        {
            id: "shuffle",
            name: (
                <>
                    <Text language="eng">Shuffle</Text>
                    <Text language="heb">ערבוב</Text>
                </>
            ),
            sort: (items, meeting) => {
                items = [...items];
                const meetingHash = parseInt(core.string.hash(meeting));
                let hashId = function (userId) {
                    const idHash = parseInt(core.string.hash(userId));
                    return meetingHash * idHash;
                };
                items.sort((a, b) => {
                    return hashId(a.user_id) - hashId(b.user_id);
                });
                return items;
            }
        }
    ];
    me.filters = [
        {
            id: "online",
            name: (
                <>
                    <Text language="eng">Online</Text>
                    <Text language="heb">אונליין</Text>
                </>
            ),
            filter: (items) => {
                items = items.filter(item => item.count > 0);
                return items;
            }
        },
        {
            id: "meeting",
            name: (
                <>
                    <Text language="eng">Meeting</Text>
                    <Text language="heb">פגישה</Text>
                </>
            ),
            filter: (items) => {
                items = items.filter(item => item.current);
                return items;
            }
        },
        {
            id: "speak",
            name: (
                <>
                    <Text language="eng">Can Speak</Text>
                    <Text language="heb">יכול לדבר</Text>
                </>
            ),
            filter: (items) => {
                items = items.filter(item => {
                    return !(["listening", "work"].find(term => item.user_name.toLowerCase().includes(term)));
                });
                return items;
            }
        }
    ];
    me.launch = async function () {
        if (core.property.get(me.singleton, "ui.node.parent")) {
            core.property.set(me.singleton, "widget.window.show", true);
            return me.singleton;
        }
        me.singleton = ui.element.create(me.json, "workspace", "self");
    };
    me.loadMeetings = async function () {
        me.meetings = await lib.zoom.meetings();
        if (me.redraw) {
            me.redraw();
        }
    };
    me.initOptions = async function (object) {
        var window = widget.window.get(object);
        ui.options.load(me, window, {
            room: ""
        });
        core.property.set(window, "app", me);
        core.property.set(window, "name", "");
    };
    me.visibilityChange = function () {
        const visibilityState = ui.session.visibilityState();
        if (me.singleton && visibilityState === "visible") {
            me.loadMeetings();
        }
    };
    me.render = function (object) {
        return (<Main />);
    };
    me.resize = function (object) {

    };
};
<|endoftext|>


<commit_before>// authentication and authorization functions
var Client = require('cas.js');
var url = require('url');
var ad = require('../config/ad.json');
var auth = require('../config/auth.json');
var pause = require('pause');

var apiUsers = require('../config/api.json');

var ldapClient = require('../lib/ldap-client');

var mongoose = require('mongoose');
var User = mongoose.model('User');

// validation of ticket is with the lan, and therefore url does not need to be proxied.
var cas = new Client({
  base_url: auth.cas,
  service: auth.service,
  version: 1.0
});

function proxied(req, res, next) {
  if (req.get('x-forwarded-host') && req.get('x-forwarded-host') === auth.proxy) {
    req.proxied = true;
    req.proxied_prefix = url.parse(auth.proxied_service).pathname;
  }
  next();
}

function cn(s) {
  var first = s.split(',', 1)[0];
  return first.substr(3).toLowerCase();
}

function filterGroup(a) {
  var output = [];
  var i;
  var group;
  for (i = 0; i < a.length; i += 1) {
    group = cn(a[i]);
    if (group.indexOf('lab.frib') === 0) {
      output.push(group);
    }
  }
  return output;
}

function ensureAuthenticated(req, res, next) {
  // console.log(req.headers);
  var ticketUrl = url.parse(req.url, true);
  if (req.session.userid) {
    // console.log(req.session);
    if (req.query.ticket) {
      // remove the ticket query param
      delete ticketUrl.query.ticket;
      return res.redirect(301, url.format({
        pathname: req.proxied ? url.resolve(auth.proxied_service + '/', '.' + ticketUrl.pathname) : ticketUrl.pathname,
        query: ticketUrl.query
      }));
    }
    next();
  } else if (req.query.ticket) {
    var halt = pause(req);

    if (req.proxied) {
      cas.service = auth.proxied_service;
    } else {
      cas.service = auth.service;
    }
    // validate directly at cas.base_url
    cas.validate(req.query.ticket, function (err, casresponse, result) {
      if (err) {
        console.error(err.message);
        res.send(401, err.message);
      } else {
        if (result.validated) {
          var userid = result.username;
          req.session.userid = userid;
          var searchFilter = ad.searchFilter.replace('_id', userid);
          var opts = {
            filter: searchFilter,
            attributes: ad.memberAttributes,
            scope: 'sub'
          };

          // query ad about other attribute
          ldapClient.search(ad.searchBase, opts, false, function (err, result) {
            if (err) {
              console.err(err.name + ' : ' + err.message);
              return res.send(500, 'something wrong with ad');
            }
            if (result.length === 0) {
              console.warn('cannot find ' + userid);
              return res.send(500, userid + ' is not found!');
            }
            if (result.length > 1) {
              return res.send(500, userid + ' is not unique!');
            }

            // set session
            req.session.username = result[0].displayName;
            req.session.memberOf = filterGroup(result[0].memberOf);

            // console.log(req.session);
            // load others from db
            User.findOne({
              _id: userid
            }).lean().exec(function (err, user) {
              if (err) {
                console.error(err.msg);
                // return res.send(500, 'internal error with db');
              }
              if (user) {
                req.session.roles = user.roles;
                // update user
                User.findByIdAndUpdate(user._id, {
                  lastVisitedOn: Date.now()
                }, function (err, update) {
                  if (err) {
                    console.err(err.message);
                    // return res.send(500, 'internal error with db');
                  }
                });
              } else {
                // create a new user
                req.session.roles = [];
                var first = new User({
                  _id: userid,
                  name: result[0].displayName,
                  email: result[0].mail,
                  office: result[0].physicalDeliveryOfficeName,
                  phone: result[0].telephoneNumber,
                  mobile: result[0].mobile,
                  roles: [],
                  lastVisitedOn: Date.now()
                });

                first.save(function (err, newUser) {
                  if (err) {
                    console.err(err.msg);
                    // return res.send(500, 'internal error with db');
                  }
                  console.info('A new user created : ' + newUser.name);
                });
              }
              if (req.session.landing && req.session.landing !== '/') {
                return res.redirect(req.proxied ? url.resolve(auth.proxied_service + '/', '.' + req.session.landing) : req.session.landing);
              }
              next();
              halt.resume();
            });
          });
        } else {
          console.error('CAS reject this ticket');
          return res.redirect(req.proxied ? auth.proxied_service : auth.service);
        }
      }
    });
  } else {
    // if this is ajax call, then kick back without redirect
    if (req.xhr) {
      // TODO: might need to properly set the WWW-Authenticate header
      res.set('WWW-Authenticate', 'CAS realm="' + (req.proxied ? auth.proxied_service : auth.service) + '"');
      res.send(401, 'xhr cannot be authenticated');
    } else {
      req.session.landing = req.url;
      if (req.proxied) {
        res.redirect(auth.proxied_cas + '/login?service=' + encodeURIComponent(auth.proxied_service));
      } else {
        res.redirect(auth.cas + '/login?service=' + encodeURIComponent(auth.service));
      }
    }
  }
}

function verifyRole(role) {
  return function (req, res, next) {
    // console.log(req.session);
    if (req.session.roles) {
      if (req.session.roles.indexOf(role) > -1) {
        next();
      } else {
        res.send(403, "You are not authorized to access this resource. ");
      }
    } else {
      console.log("Cannot find the user's role.");
      res.send(500, "something wrong for the user's session");
    }
  };
}

var basic = require('basic-auth');

function notKnown(cred) {
  if (apiUsers.hasOwnProperty(cred.name)) {
    if (apiUsers[cred.name] === cred.pass) {
      return false;
    }
  }
  return true;
}

function basicAuth(req, res, next) {
  var cred = basic(req);
  if (!cred || notKnown(cred)) {
    res.set('WWW-Authenticate', 'Basic realm="api"');
    return res.send(401);
  }
  next();
}

module.exports = {
  ensureAuthenticated: ensureAuthenticated,
  verifyRole: verifyRole,
  basicAuth: basicAuth,
  proxied: proxied
};
<commit_msg>fix typo<commit_after>// authentication and authorization functions
var Client = require('cas.js');
var url = require('url');
var ad = require('../config/ad.json');
var auth = require('../config/auth.json');
var pause = require('pause');

var apiUsers = require('../config/api.json');

var ldapClient = require('../lib/ldap-client');

var mongoose = require('mongoose');
var User = mongoose.model('User');

// validation of ticket is with the lan, and therefore url does not need to be proxied.
var cas = new Client({
  base_url: auth.cas,
  service: auth.service,
  version: 1.0
});

function proxied(req, res, next) {
  if (req.get('x-forwarded-host') && req.get('x-forwarded-host') === auth.proxy) {
    req.proxied = true;
    req.proxied_prefix = url.parse(auth.proxied_service).pathname;
  }
  next();
}

function cn(s) {
  var first = s.split(',', 1)[0];
  return first.substr(3).toLowerCase();
}

function filterGroup(a) {
  var output = [];
  var i;
  var group;
  for (i = 0; i < a.length; i += 1) {
    group = cn(a[i]);
    if (group.indexOf('lab.frib') === 0) {
      output.push(group);
    }
  }
  return output;
}

function ensureAuthenticated(req, res, next) {
  // console.log(req.headers);
  var ticketUrl = url.parse(req.url, true);
  if (req.session.userid) {
    // console.log(req.session);
    if (req.query.ticket) {
      // remove the ticket query param
      delete ticketUrl.query.ticket;
      return res.redirect(301, url.format({
        pathname: req.proxied ? url.resolve(auth.proxied_service + '/', '.' + ticketUrl.pathname) : ticketUrl.pathname,
        query: ticketUrl.query
      }));
    }
    next();
  } else if (req.query.ticket) {
    var halt = pause(req);

    if (req.proxied) {
      cas.service = auth.proxied_service;
    } else {
      cas.service = auth.service;
    }
    // validate directly at cas.base_url
    cas.validate(req.query.ticket, function (err, casresponse, result) {
      if (err) {
        console.error(err.message);
        res.send(401, err.message);
      } else {
        if (result.validated) {
          var userid = result.username;
          req.session.userid = userid;
          var searchFilter = ad.searchFilter.replace('_id', userid);
          var opts = {
            filter: searchFilter,
            attributes: ad.memberAttributes,
            scope: 'sub'
          };

          // query ad about other attribute
          ldapClient.search(ad.searchBase, opts, false, function (err, result) {
            if (err) {
              console.error(err.name + ' : ' + err.message);
              return res.send(500, 'something wrong with ad');
            }
            if (result.length === 0) {
              console.warn('cannot find ' + userid);
              return res.send(500, userid + ' is not found!');
            }
            if (result.length > 1) {
              return res.send(500, userid + ' is not unique!');
            }

            // set session
            req.session.username = result[0].displayName;
            req.session.memberOf = filterGroup(result[0].memberOf);

            // console.log(req.session);
            // load others from db
            User.findOne({
              _id: userid
            }).lean().exec(function (err, user) {
              if (err) {
                console.error(err.msg);
                // return res.send(500, 'internal error with db');
              }
              if (user) {
                req.session.roles = user.roles;
                // update user
                User.findByIdAndUpdate(user._id, {
                  lastVisitedOn: Date.now()
                }, function (err, update) {
                  if (err) {
                    console.error(err.message);
                    // return res.send(500, 'internal error with db');
                  }
                });
              } else {
                // create a new user
                req.session.roles = [];
                var first = new User({
                  _id: userid,
                  name: result[0].displayName,
                  email: result[0].mail,
                  office: result[0].physicalDeliveryOfficeName,
                  phone: result[0].telephoneNumber,
                  mobile: result[0].mobile,
                  roles: [],
                  lastVisitedOn: Date.now()
                });

                first.save(function (err, newUser) {
                  if (err) {
                    console.error(err.msg);
                    // return res.send(500, 'internal error with db');
                  }
                  console.info('A new user created : ' + newUser.name);
                });
              }
              if (req.session.landing && req.session.landing !== '/') {
                return res.redirect(req.proxied ? url.resolve(auth.proxied_service + '/', '.' + req.session.landing) : req.session.landing);
              }
              next();
              halt.resume();
            });
          });
        } else {
          console.error('CAS reject this ticket');
          return res.redirect(req.proxied ? auth.proxied_service : auth.service);
        }
      }
    });
  } else {
    // if this is ajax call, then kick back without redirect
    if (req.xhr) {
      // TODO: might need to properly set the WWW-Authenticate header
      res.set('WWW-Authenticate', 'CAS realm="' + (req.proxied ? auth.proxied_service : auth.service) + '"');
      res.send(401, 'xhr cannot be authenticated');
    } else {
      req.session.landing = req.url;
      if (req.proxied) {
        res.redirect(auth.proxied_cas + '/login?service=' + encodeURIComponent(auth.proxied_service));
      } else {
        res.redirect(auth.cas + '/login?service=' + encodeURIComponent(auth.service));
      }
    }
  }
}

function verifyRole(role) {
  return function (req, res, next) {
    // console.log(req.session);
    if (req.session.roles) {
      if (req.session.roles.indexOf(role) > -1) {
        next();
      } else {
        res.send(403, "You are not authorized to access this resource. ");
      }
    } else {
      console.log("Cannot find the user's role.");
      res.send(500, "something wrong for the user's session");
    }
  };
}

var basic = require('basic-auth');

function notKnown(cred) {
  if (apiUsers.hasOwnProperty(cred.name)) {
    if (apiUsers[cred.name] === cred.pass) {
      return false;
    }
  }
  return true;
}

function basicAuth(req, res, next) {
  var cred = basic(req);
  if (!cred || notKnown(cred)) {
    res.set('WWW-Authenticate', 'Basic realm="api"');
    return res.send(401);
  }
  next();
}

module.exports = {
  ensureAuthenticated: ensureAuthenticated,
  verifyRole: verifyRole,
  basicAuth: basicAuth,
  proxied: proxied
};
<|endoftext|>


<commit_before>// Module imports
import '@babel/polyfill';
import keytar from 'keytar';
import SemVer from 'semver';
import url from 'url';
import https from 'https';
import { app, dialog, ipcMain, session, shell } from 'electron';
import { autoUpdater } from 'electron-updater';
import { Lbry } from 'lbry-redux';
import Daemon from './Daemon';
import isDev from 'electron-is-dev';
import createTray from './createTray';
import createWindow from './createWindow';
import pjson from '../../../package.json';
import startSandbox from './startSandbox';
import installDevtools from './installDevtools';

autoUpdater.autoDownload = true;

// This is set to true if an auto update has been downloaded through the Electron
// auto-update system and is ready to install. If the user declined an update earlier,
// it will still install on shutdown.
let autoUpdateDownloaded = false;

// This is used to keep track of whether we are showing the special dialog
// that we show on Windows after you decline an upgrade and close the app later.
let showingAutoUpdateCloseAlert = false;

// Keep a global reference, if you don't, they will be closed automatically when the JavaScript
// object is garbage collected.
let rendererWindow;

let tray;
let daemon;

const appState = {};

app.setAsDefaultProtocolClient('lbry');
app.setName('LBRY');
app.setAppUserModelId('io.lbry.LBRY');

if (isDev) {
  // Disable security warnings in dev mode:
  // https://github.com/electron/electron/blob/master/docs/tutorial/security.md#electron-security-warnings
  process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = true;
}

const startDaemon = async () => {
  let isDaemonRunning = false;

  await Lbry.status()
    .then(() => {
      isDaemonRunning = true;
      console.log('SDK already running');
    })
    .catch(() => {
      console.log('Starting SDK');
    });

  if (!isDaemonRunning) {
    daemon = new Daemon();
    daemon.on('exit', () => {
      if (!isDev) {
        daemon = null;
        if (!appState.isQuitting) {
          dialog.showErrorBox(
            'Daemon has Exited',
            'The daemon may have encountered an unexpected error, or another daemon instance is already running. \n\n' +
              'For more information please visit: \n' +
              'https://lbry.com/faq/startup-troubleshooting'
          );
        }
        app.quit();
      }
    });
    await daemon.launch();
  }
};

// When we are starting the app, ensure there are no other apps already running
const gotSingleInstanceLock = app.requestSingleInstanceLock();

if (!gotSingleInstanceLock) {
  // Another instance already has a lock, abort
  app.quit();
} else {
  app.on('second-instance', (event, argv) => {
    // Send the url to the app to navigate first, then focus
    if (rendererWindow) {
      if ((process.platform === 'win32' || process.platform === 'linux') && String(argv[1]).startsWith('lbry')) {
        let URI = argv[1];

        // Keep only command line / deep linked arguments
        // Windows normalizes URIs when they're passed in from other apps. On Windows, this tries to
        // restore the original URI that was typed.
        //   - If the URI has no path, Windows adds a trailing slash. LBRY URIs can't have a slash with no
        //     path, so we just strip it off.
        //   - In a URI with a claim ID, like lbry://channel#claimid, Windows interprets the hash mark as
        //     an anchor and converts it to lbry://channel/#claimid. We remove the slash here as well.
        //   - ? also interpreted as an anchor, remove slash also.
        if (process.platform === 'win32') {
          URI = URI.replace(/\/$/, '')
            .replace('/#', '#')
            .replace('/?', '?');
        }

        rendererWindow.webContents.send('open-uri-requested', URI);
      }

      rendererWindow.show();
    }
  });

  app.on('ready', async () => {
    await startDaemon();
    startSandbox();

    if (isDev) {
      await installDevtools();
    }

    rendererWindow = createWindow(appState);
    tray = createTray(rendererWindow);

    if (!isDev) {
      rendererWindow.webContents.on('devtools-opened', () => {
        // Send a message to the renderer process so we can log a security warning
        rendererWindow.webContents.send('devtools-is-opened');
      });
    }

    // HACK: patch webrequest to fix devtools incompatibility with electron 2.x.
    // See https://github.com/electron/electron/issues/13008#issuecomment-400261941
    session.defaultSession.webRequest.onBeforeRequest({}, (details, callback) => {
      if (details.url.indexOf('7accc8730b0f99b5e7c0702ea89d1fa7c17bfe33') !== -1) {
        callback({
          redirectURL: details.url.replace(
            '7accc8730b0f99b5e7c0702ea89d1fa7c17bfe33',
            '57c9d07b416b5a2ea23d28247300e4af36329bdc'
          ),
        });
      } else {
        callback({ cancel: false });
      }
    });
  });
}

app.on('activate', () => {
  if (rendererWindow) {
    rendererWindow.show();
  }
});

app.on('will-quit', event => {
  if (
    process.platform === 'win32' &&
    autoUpdateDownloaded &&
    !appState.autoUpdateAccepted &&
    !showingAutoUpdateCloseAlert
  ) {
    // We're on Win and have an update downloaded, but the user declined it (or closed
    // the app without accepting it). Now the user is closing the app, so the new update
    // will install. On Mac this is silent, but on Windows they get a confusing permission
    // escalation dialog, so we show Windows users a warning dialog first.

    showingAutoUpdateCloseAlert = true;
    dialog.showMessageBox(
      {
        type: 'info',
        title: 'LBRY Will Upgrade',
        message: 'LBRY has a pending upgrade. Please select "Yes" to install it on the prompt shown after this one.',
      },
      () => {
        app.quit();
      }
    );

    event.preventDefault();
    return;
  }

  appState.isQuitting = true;
  if (daemon) {
    daemon.quit();
    event.preventDefault();
  }

  if (rendererWindow) {
    rendererWindow = null;
  }
});

app.on('before-quit', () => {
  appState.isQuitting = true;
});

ipcMain.on('upgrade', (event, installerPath) => {
  app.on('quit', () => {
    console.log('Launching upgrade installer at', installerPath);
    // This gets triggered called after *all* other quit-related events, so
    // we'll only get here if we're fully prepared and quitting for real.
    shell.openItem(installerPath);
  });
  // what to do if no shutdown in a long time?
  console.log('Update downloaded to', installerPath);
  console.log('The app will close, and you will be prompted to install the latest version of LBRY.');
  console.log('After the install is complete, please reopen the app.');
  app.quit();
});

autoUpdater.on('update-downloaded', () => {
  autoUpdateDownloaded = true;
});

ipcMain.on('autoUpdateAccepted', () => {
  appState.autoUpdateAccepted = true;
  autoUpdater.quitAndInstall();
});

ipcMain.on('version-info-requested', () => {
  function formatRc(ver) {
    // Adds dash if needed to make RC suffix SemVer friendly
    return ver.replace(/([^-])rc/, '$1-rc');
  }

  const localVersion = pjson.version;
  let result = '';
  const onSuccess = res => {
    res.on('data', data => {
      result += data;
    });

    res.on('end', () => {
      let json;
      try {
        json = JSON.parse(result);
      } catch (e) {
        return;
      }
      const tagName = json.tag_name;
      if (tagName) {
        const [, remoteVersion] = tagName.match(/^v([\d.]+(?:-?rc\d+)?)$/);
        if (!remoteVersion) {
          if (rendererWindow) {
            rendererWindow.webContents.send('version-info-received', localVersion);
          }
        } else {
          const upgradeAvailable = SemVer.gt(formatRc(remoteVersion), formatRc(localVersion));
          if (rendererWindow) {
            rendererWindow.webContents.send('version-info-received', {
              remoteVersion,
              localVersion,
              upgradeAvailable,
            });
          }
        }
      } else if (rendererWindow) {
        rendererWindow.webContents.send('version-info-received', localVersion);
      }
    });
  };

  const requestLatestRelease = (alreadyRedirected = false) => {
    const req = https.get(
      {
        hostname: 'api.github.com',
        path: '/repos/lbryio/lbry-desktop/releases/latest',
        headers: { 'user-agent': `LBRY/${localVersion}` },
      },
      res => {
        if (res.statusCode === 301 || res.statusCode === 302) {
          requestLatestRelease(res.headers.location, true);
        } else {
          onSuccess(res);
        }
      }
    );

    if (alreadyRedirected) return;
    req.on('error', err => {
      console.log('Failed to get current version from GitHub. Error:', err);
      if (rendererWindow) {
        rendererWindow.webContents.send('version-info-received', null);
      }
    });
  };

  requestLatestRelease();
});

ipcMain.on('get-auth-token', event => {
  keytar.getPassword('LBRY', 'auth_token').then(token => {
    event.sender.send('auth-token-response', token ? token.toString().trim() : null);
  });
});

ipcMain.on('set-auth-token', (event, token) => {
  keytar.setPassword('LBRY', 'auth_token', token ? token.toString().trim() : null);
});

process.on('uncaughtException', error => {
  console.log(error);
  dialog.showErrorBox('Error Encountered', `Caught error: ${error}`);
  appState.isQuitting = true;
  if (daemon) daemon.quit();
  app.exit(1);
});
<commit_msg>fixes mac openlinks<commit_after>// Module imports
import '@babel/polyfill';
import keytar from 'keytar';
import SemVer from 'semver';
import https from 'https';
import { app, dialog, ipcMain, session, shell } from 'electron';
import { autoUpdater } from 'electron-updater';
import { Lbry } from 'lbry-redux';
import Daemon from './Daemon';
import isDev from 'electron-is-dev';
import createTray from './createTray';
import createWindow from './createWindow';
import pjson from '../../../package.json';
import startSandbox from './startSandbox';
import installDevtools from './installDevtools';

autoUpdater.autoDownload = true;

// This is set to true if an auto update has been downloaded through the Electron
// auto-update system and is ready to install. If the user declined an update earlier,
// it will still install on shutdown.
let autoUpdateDownloaded = false;

// This is used to keep track of whether we are showing the special dialog
// that we show on Windows after you decline an upgrade and close the app later.
let showingAutoUpdateCloseAlert = false;

// Keep a global reference, if you don't, they will be closed automatically when the JavaScript
// object is garbage collected.
let rendererWindow;

let tray;
let daemon;

const appState = {};

app.setAsDefaultProtocolClient('lbry');
app.setName('LBRY');
app.setAppUserModelId('io.lbry.LBRY');

if (isDev) {
  // Disable security warnings in dev mode:
  // https://github.com/electron/electron/blob/master/docs/tutorial/security.md#electron-security-warnings
  process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = true;
}

const startDaemon = async () => {
  let isDaemonRunning = false;

  await Lbry.status()
    .then(() => {
      isDaemonRunning = true;
      console.log('SDK already running');
    })
    .catch(() => {
      console.log('Starting SDK');
    });

  if (!isDaemonRunning) {
    daemon = new Daemon();
    daemon.on('exit', () => {
      if (!isDev) {
        daemon = null;
        if (!appState.isQuitting) {
          dialog.showErrorBox(
            'Daemon has Exited',
            'The daemon may have encountered an unexpected error, or another daemon instance is already running. \n\n' +
              'For more information please visit: \n' +
              'https://lbry.com/faq/startup-troubleshooting'
          );
        }
        app.quit();
      }
    });
    await daemon.launch();
  }
};

// When we are starting the app, ensure there are no other apps already running
const gotSingleInstanceLock = app.requestSingleInstanceLock();

if (!gotSingleInstanceLock) {
  // Another instance already has a lock, abort
  app.quit();
} else {
  app.on('second-instance', (event, argv) => {
    // Send the url to the app to navigate first, then focus
    if (rendererWindow) {
      if ((process.platform === 'win32' || process.platform === 'linux') && String(argv[1]).startsWith('lbry')) {
        let URI = argv[1];

        // Keep only command line / deep linked arguments
        // Windows normalizes URIs when they're passed in from other apps. On Windows, this tries to
        // restore the original URI that was typed.
        //   - If the URI has no path, Windows adds a trailing slash. LBRY URIs can't have a slash with no
        //     path, so we just strip it off.
        //   - In a URI with a claim ID, like lbry://channel#claimid, Windows interprets the hash mark as
        //     an anchor and converts it to lbry://channel/#claimid. We remove the slash here as well.
        //   - ? also interpreted as an anchor, remove slash also.
        if (process.platform === 'win32') {
          URI = URI.replace(/\/$/, '')
            .replace('/#', '#')
            .replace('/?', '?');
        }

        rendererWindow.webContents.send('open-uri-requested', URI);
      }

      rendererWindow.show();
    }
  });

  app.on('ready', async () => {
    await startDaemon();
    startSandbox();

    if (isDev) {
      await installDevtools();
    }

    rendererWindow = createWindow(appState);
    tray = createTray(rendererWindow);

    if (!isDev) {
      rendererWindow.webContents.on('devtools-opened', () => {
        // Send a message to the renderer process so we can log a security warning
        rendererWindow.webContents.send('devtools-is-opened');
      });
    }

    // HACK: patch webrequest to fix devtools incompatibility with electron 2.x.
    // See https://github.com/electron/electron/issues/13008#issuecomment-400261941
    session.defaultSession.webRequest.onBeforeRequest({}, (details, callback) => {
      if (details.url.indexOf('7accc8730b0f99b5e7c0702ea89d1fa7c17bfe33') !== -1) {
        callback({
          redirectURL: details.url.replace(
            '7accc8730b0f99b5e7c0702ea89d1fa7c17bfe33',
            '57c9d07b416b5a2ea23d28247300e4af36329bdc'
          ),
        });
      } else {
        callback({ cancel: false });
      }
    });
  });
}

app.on('activate', () => {
  if (rendererWindow) {
    rendererWindow.show();
  }
});

app.on('will-quit', event => {
  if (
    process.platform === 'win32' &&
    autoUpdateDownloaded &&
    !appState.autoUpdateAccepted &&
    !showingAutoUpdateCloseAlert
  ) {
    // We're on Win and have an update downloaded, but the user declined it (or closed
    // the app without accepting it). Now the user is closing the app, so the new update
    // will install. On Mac this is silent, but on Windows they get a confusing permission
    // escalation dialog, so we show Windows users a warning dialog first.

    showingAutoUpdateCloseAlert = true;
    dialog.showMessageBox(
      {
        type: 'info',
        title: 'LBRY Will Upgrade',
        message: 'LBRY has a pending upgrade. Please select "Yes" to install it on the prompt shown after this one.',
      },
      () => {
        app.quit();
      }
    );

    event.preventDefault();
    return;
  }

  appState.isQuitting = true;
  if (daemon) {
    daemon.quit();
    event.preventDefault();
  }

  if (rendererWindow) {
    rendererWindow = null;
  }
});

app.on('will-finish-launching', () => {
  // Protocol handler for macOS
  app.on('open-url', (event, URL) => {
    event.preventDefault();

    if (rendererWindow) {
      rendererWindow.webContents.send('open-uri-requested', URL);
      rendererWindow.show();
    } else {
      appState.macDeepLinkingURI = URL;
    }
  });
});

app.on('before-quit', () => {
  appState.isQuitting = true;
});

ipcMain.on('upgrade', (event, installerPath) => {
  app.on('quit', () => {
    console.log('Launching upgrade installer at', installerPath);
    // This gets triggered called after *all* other quit-related events, so
    // we'll only get here if we're fully prepared and quitting for real.
    shell.openItem(installerPath);
  });
  // what to do if no shutdown in a long time?
  console.log('Update downloaded to', installerPath);
  console.log('The app will close, and you will be prompted to install the latest version of LBRY.');
  console.log('After the install is complete, please reopen the app.');
  app.quit();
});

autoUpdater.on('update-downloaded', () => {
  autoUpdateDownloaded = true;
});

ipcMain.on('autoUpdateAccepted', () => {
  appState.autoUpdateAccepted = true;
  autoUpdater.quitAndInstall();
});

ipcMain.on('version-info-requested', () => {
  function formatRc(ver) {
    // Adds dash if needed to make RC suffix SemVer friendly
    return ver.replace(/([^-])rc/, '$1-rc');
  }

  const localVersion = pjson.version;
  let result = '';
  const onSuccess = res => {
    res.on('data', data => {
      result += data;
    });

    res.on('end', () => {
      let json;
      try {
        json = JSON.parse(result);
      } catch (e) {
        return;
      }
      const tagName = json.tag_name;
      if (tagName) {
        const [, remoteVersion] = tagName.match(/^v([\d.]+(?:-?rc\d+)?)$/);
        if (!remoteVersion) {
          if (rendererWindow) {
            rendererWindow.webContents.send('version-info-received', localVersion);
          }
        } else {
          const upgradeAvailable = SemVer.gt(formatRc(remoteVersion), formatRc(localVersion));
          if (rendererWindow) {
            rendererWindow.webContents.send('version-info-received', {
              remoteVersion,
              localVersion,
              upgradeAvailable,
            });
          }
        }
      } else if (rendererWindow) {
        rendererWindow.webContents.send('version-info-received', localVersion);
      }
    });
  };

  const requestLatestRelease = (alreadyRedirected = false) => {
    const req = https.get(
      {
        hostname: 'api.github.com',
        path: '/repos/lbryio/lbry-desktop/releases/latest',
        headers: { 'user-agent': `LBRY/${localVersion}` },
      },
      res => {
        if (res.statusCode === 301 || res.statusCode === 302) {
          requestLatestRelease(res.headers.location, true);
        } else {
          onSuccess(res);
        }
      }
    );

    if (alreadyRedirected) return;
    req.on('error', err => {
      console.log('Failed to get current version from GitHub. Error:', err);
      if (rendererWindow) {
        rendererWindow.webContents.send('version-info-received', null);
      }
    });
  };

  requestLatestRelease();
});

ipcMain.on('get-auth-token', event => {
  keytar.getPassword('LBRY', 'auth_token').then(token => {
    event.sender.send('auth-token-response', token ? token.toString().trim() : null);
  });
});

ipcMain.on('set-auth-token', (event, token) => {
  keytar.setPassword('LBRY', 'auth_token', token ? token.toString().trim() : null);
});

process.on('uncaughtException', error => {
  console.log(error);
  dialog.showErrorBox('Error Encountered', `Caught error: ${error}`);
  appState.isQuitting = true;
  if (daemon) daemon.quit();
  app.exit(1);
});
<|endoftext|>