<commit_before>(function($) {

	$.extend(mejs.MepDefaults, {
		usePluginFullScreen: true,
		newWindowCallback: function() { return '';},
		fullscreenText: mejs.i18n.t('Fullscreen')
	});

	$.extend(MediaElementPlayer.prototype, {

		isFullScreen: false,

		isNativeFullScreen: false,

		docStyleOverflow: null,

		isInIframe: false,

		buildfullscreen: function(player, controls, layers, media) {

			if (!player.isVideo)
				return;

			player.isInIframe = (window.location != window.parent.location);

			// native events
			if (mejs.MediaFeatures.hasTrueNativeFullScreen) {

				// chrome doesn't alays fire this in an iframe
				var func = function(e) {

					if (mejs.MediaFeatures.isFullScreen()) {
						player.isNativeFullScreen = true;
						// reset the controls once we are fully in full screen
						player.setControlsSize();
					} else {
						player.isNativeFullScreen = false;
						// when a user presses ESC
						// make sure to put the player back into place
						player.exitFullScreen();
					}
				};

				if (mejs.MediaFeatures.hasMozNativeFullScreen) {
					player.globalBind(mejs.MediaFeatures.fullScreenEventName, func);
				} else {
					player.container.bind(mejs.MediaFeatures.fullScreenEventName, func);
				}
			}

			var t = this,
				normalHeight = 0,
				normalWidth = 0,
				container = player.container,
				fullscreenBtn =
					$('
' +
						'<button type="button" aria-controls="' + t.id + '" title="' + t.options.fullscreenText + '" aria-label="' + t.options.fullscreenText + '"></button>' +
					'
')
					.appendTo(controls);

				if (t.media.pluginType === 'native' || (!t.options.usePluginFullScreen && !mejs.MediaFeatures.isFirefox)) {

					fullscreenBtn.click(function() {
						var isFullScreen = (mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || player.isFullScreen;

						if (isFullScreen) {
							player.exitFullScreen();
						} else {
							player.enterFullScreen();
						}
					});

				} else {

					var hideTimeout = null,
						supportsPointerEvents = (function() {
							// TAKEN FROM MODERNIZR
							var element = document.createElement('x'),
								documentElement = document.documentElement,
								getComputedStyle = window.getComputedStyle,
								supports;
							if(!('pointerEvents' in element.style)){
								return false;
							}
							element.style.pointerEvents = 'auto';
							element.style.pointerEvents = 'x';
							documentElement.appendChild(element);
							supports = getComputedStyle &&
								getComputedStyle(element, '').pointerEvents === 'auto';
							documentElement.removeChild(element);
							return !!supports;
						})();

					//console.log('supportsPointerEvents', supportsPointerEvents);

					if (supportsPointerEvents && !mejs.MediaFeatures.isOpera) { // opera doesn't allow this :(

						// allows clicking through the fullscreen button and controls down directly to Flash

						/*
						 When a user puts his mouse over the fullscreen button, the controls are disabled
						 So we put a div over the video and another one on iether side of the fullscreen button
						 that caputre mouse movement
						 and restore the controls once the mouse moves outside of the fullscreen button
						*/

						var fullscreenIsDisabled = false,
							restoreControls = function() {
								if (fullscreenIsDisabled) {
									// hide the hovers
									for (var i in hoverDivs) {
										hoverDivs[i].hide();
									}

									// restore the control bar
									fullscreenBtn.css('pointer-events', '');
									t.controls.css('pointer-events', '');

                  // prevent clicks from pausing video
                  t.media.removeEventListener('click', t.clickToPlayPauseCallback);

									// store for later
									fullscreenIsDisabled = false;
								}
							},
							hoverDivs = {},
							hoverDivNames = ['top', 'left', 'right', 'bottom'],
							i, len,
							positionHoverDivs = function() {
								var fullScreenBtnOffsetLeft = fullscreenBtn.offset().left - t.container.offset().left,
									fullScreenBtnOffsetTop = fullscreenBtn.offset().top - t.container.offset().top,
									fullScreenBtnWidth = fullscreenBtn.outerWidth(true),
									fullScreenBtnHeight = fullscreenBtn.outerHeight(true),
									containerWidth = t.container.width(),
									containerHeight = t.container.height();

							  for (i in hoverDivs) {
									hoverDivs[i].css({position: 'absolute', top: 0, left: 0}); //, backgroundColor: '#f00'});
								}

								// over video, but not controls
								hoverDivs['top']
									.width( containerWidth )
									.height( fullScreenBtnOffsetTop );

								// over controls, but not the fullscreen button
								hoverDivs['left']
									.width( fullScreenBtnOffsetLeft )
									.height( fullScreenBtnHeight )
									.css({top: fullScreenBtnOffsetTop});

								// after the fullscreen button
								hoverDivs['right']
									.width( containerWidth - fullScreenBtnOffsetLeft - fullScreenBtnWidth )
									.height( fullScreenBtnHeight )
									.css({top: fullScreenBtnOffsetTop,
										 left: fullScreenBtnOffsetLeft + fullScreenBtnWidth});

								// under the fullscreen button
								hoverDivs['bottom']
									.width( containerWidth )
									.height( containerHeight - fullScreenBtnHeight - fullScreenBtnOffsetTop )
									.css({top: fullScreenBtnOffsetTop + fullScreenBtnHeight});
							};

						t.globalBind('resize', function() {
							positionHoverDivs();
						});

						for (i = 0, len = hoverDivNames.length; i < len; i += 1) {
							hoverDivs[hoverDivNames[i]] = $('
').appendTo(t.container).mouseover(restoreControls).hide();
						}

						// on hover, kill the fullscreen button's HTML handling, allowing clicks down to Flash
						fullscreenBtn
							.mouseover(function() {

								if (!t.isFullScreen) {

									var buttonPos = fullscreenBtn.offset(),
										containerPos = player.container.offset();

									// move the button in Flash into place
									media.positionFullscreenButton(buttonPos.left - containerPos.left, buttonPos.top - containerPos.top, false);

									// allows click through
									fullscreenBtn.css('pointer-events', 'none');
									t.controls.css('pointer-events', 'none');

                  // restore click-to-play
                  t.media.addEventListener('click', t.clickToPlayPauseCallback);

									// show the divs that will restore things
								  for (i in hoverDivs) {
										hoverDivs[i].show();
									}
									positionHoverDivs();

									fullscreenIsDisabled = true;
								}

							});

						// restore controls anytime the user enters or leaves fullscreen
						media.addEventListener('fullscreenchange', function(e) {
							t.isFullScreen = !t.isFullScreen;
							// don't allow plugin click to pause video - messes with
							// plugin's controls
              if (t.isFullScreen) {
                t.media.removeEventListener('click', t.clickToPlayPauseCallback);
              } else {
                t.media.addEventListener('click', t.clickToPlayPauseCallback);
              }
							restoreControls();
						});

						// the mouseout event doesn't work on the fullscren button, because we already killed the pointer-events
						// so we use the document.mousemove event to restore controls when the mouse moves outside the fullscreen button
						/*
						t.globalBind('mousemove', function(e) {

							// if the mouse is anywhere but the fullsceen button, then restore it all
							if (fullscreenIsDisabled) {

								var fullscreenBtnPos = fullscreenBtn.offset();

								if (e.pageY < fullscreenBtnPos.top || e.pageY > fullscreenBtnPos.top + fullscreenBtn.outerHeight(true) ||
									e.pageX < fullscreenBtnPos.left || e.pageX > fullscreenBtnPos.left + fullscreenBtn.outerWidth(true)
									) {

									fullscreenBtn.css('pointer-events', '');
									t.controls.css('pointer-events', '');

									fullscreenIsDisabled = false;
								}
							}
						});
						*/

					} else {

						// the hover state will show the fullscreen button in Flash to hover up and click

						fullscreenBtn
							.mouseover(function() {

								if (hideTimeout !== null) {
									clearTimeout(hideTimeout);
									delete hideTimeout;
								}

								var buttonPos = fullscreenBtn.offset(),
									containerPos = player.container.offset();

								media.positionFullscreenButton(buttonPos.left - containerPos.left, buttonPos.top - containerPos.top, true);

							})
							.mouseout(function() {

								if (hideTimeout !== null) {
									clearTimeout(hideTimeout);
									delete hideTimeout;
								}

								hideTimeout = setTimeout(function() {
									media.hideFullscreenButton();
								}, 1500);

							});
					}
				}

			player.fullscreenBtn = fullscreenBtn;

			t.globalBind('keydown',function (e) {
				if (((mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || t.isFullScreen) && e.keyCode == 27) {
					player.exitFullScreen();
				}
			});

		},

		cleanfullscreen: function(player) {
			player.exitFullScreen();
		},

        containerSizeTimeout: null,

		enterFullScreen: function() {

			var t = this;

			// firefox+flash can't adjust plugin sizes without resetting :(
			if (t.media.pluginType !== 'native' && (mejs.MediaFeatures.isFirefox || t.options.usePluginFullScreen)) {
				//t.media.setFullscreen(true);
				//player.isFullScreen = true;
				return;
			}

			// store overflow
			docStyleOverflow = document.documentElement.style.overflow;
			// set it to not show scroll bars so 100% will work
			document.documentElement.style.overflow = 'hidden';

			// store sizing
			normalHeight = t.container.height();
			normalWidth = t.container.width();

			// attempt to do true fullscreen (Safari 5.1 and Firefox Nightly only for now)
			if (t.media.pluginType === 'native') {
				if (mejs.MediaFeatures.hasTrueNativeFullScreen) {

					mejs.MediaFeatures.requestFullScreen(t.container[0]);
					//return;

					if (t.isInIframe) {
						// sometimes exiting from fullscreen doesn't work
						// notably in Chrome . Fixed in version 17
						setTimeout(function checkFullscreen() {

							if (t.isNativeFullScreen) {

								// check if the video is suddenly not really fullscreen
								if ($(window).width() !== screen.width) {
									// manually exit
									t.exitFullScreen();
								} else {
									// test again
									setTimeout(checkFullscreen, 500);
								}
							}

						}, 500);
					}

				} else if (mejs.MediaFeatures.hasSemiNativeFullScreen) {
					t.media.webkitEnterFullscreen();
					return;
				}
			}

			// check for iframe launch
			if (t.isInIframe) {
				var url = t.options.newWindowCallback(this);

				if (url !== '') {

					// launch immediately
					if (!mejs.MediaFeatures.hasTrueNativeFullScreen) {
						t.pause();
						window.open(url, t.id, 'top=0,left=0,width=' + screen.availWidth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no');
						return;
					} else {
						setTimeout(function() {
							if (!t.isNativeFullScreen) {
								t.pause();
								window.open(url, t.id, 'top=0,left=0,width=' + screen.availWidth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no');
							}
						}, 250);
					}
				}

			}

			// full window code

			// make full size
			t.container
				.addClass('mejs-container-fullscreen')
				.width('100%')
				.height('100%');
				//.css({position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, overflow: 'hidden', width: '100%', height: '100%', 'z-index': 1000});

			// Only needed for safari 5.1 native full screen, can cause display issues elsewhere
			// Actually, it seems to be needed for IE8, too
			//if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
				t.containerSizeTimeout = setTimeout(function() {
					t.container.css({width: '100%', height: '100%'});
					t.setControlsSize();
				}, 500);
			//}

			if (t.pluginType === 'native') {
				t.$media
					.width('100%')
					.height('100%');
			} else {
				t.container.find('.mejs-shim')
					.width('100%')
					.height('100%');

				//if (!mejs.MediaFeatures.hasTrueNativeFullScreen) {
					t.media.setVideoSize($(window).width(),$(window).height());
				//}
			}

			t.layers.children('div')
				.width('100%')
				.height('100%');

			if (t.fullscreenBtn) {
				t.fullscreenBtn
					.removeClass('mejs-fullscreen')
					.addClass('mejs-unfullscreen');
			}

			t.setControlsSize();
			t.isFullScreen = true;
		},

		exitFullScreen: function() {

			var t = this;

            // Prevent container from attempting to stretch a second time
            clearTimeout(t.containerSizeTimeout);

			// firefox can't adjust plugins
			if (t.media.pluginType !== 'native' && mejs.MediaFeatures.isFirefox) {
				t.media.setFullscreen(false);
				//player.isFullScreen = false;
				return;
			}

			// come outo of native fullscreen
			if (mejs.MediaFeatures.hasTrueNativeFullScreen && (mejs.MediaFeatures.isFullScreen() || t.isFullScreen)) {
				mejs.MediaFeatures.cancelFullScreen();
			}

			// restore scroll bars to document
			document.documentElement.style.overflow = docStyleOverflow;

			t.container
				.removeClass('mejs-container-fullscreen')
				.width(normalWidth)
				.height(normalHeight);
				//.css({position: '', left: '', top: '', right: '', bottom: '', overflow: 'inherit', width: normalWidth + 'px', height: normalHeight + 'px', 'z-index': 1});

			if (t.pluginType === 'native') {
				t.$media
					.width(normalWidth)
					.height(normalHeight);
			} else {
				t.container.find('object embed')
					.width(normalWidth)
					.height(normalHeight);

				t.media.setVideoSize(normalWidth, normalHeight);
			}

			t.layers.children('div')
				.width(normalWidth)
				.height(normalHeight);

			t.fullscreenBtn
				.removeClass('mejs-unfullscreen')
				.addClass('mejs-fullscreen');

			t.setControlsSize();
			t.isFullScreen = false;
		}
	});

})(mejs.$);
<commit_msg>Fullscreen doesn't properly check for plugin type from https://github.com/johndyer/mediaelement/pull/865<commit_after>(function($) {

	$.extend(mejs.MepDefaults, {
		usePluginFullScreen: true,
		newWindowCallback: function() { return '';},
		fullscreenText: mejs.i18n.t('Fullscreen')
	});

	$.extend(MediaElementPlayer.prototype, {

		isFullScreen: false,

		isNativeFullScreen: false,

		docStyleOverflow: null,

		isInIframe: false,

		buildfullscreen: function(player, controls, layers, media) {

			if (!player.isVideo)
				return;

			player.isInIframe = (window.location != window.parent.location);

			// native events
			if (mejs.MediaFeatures.hasTrueNativeFullScreen) {

				// chrome doesn't alays fire this in an iframe
				var func = function(e) {

					if (mejs.MediaFeatures.isFullScreen()) {
						player.isNativeFullScreen = true;
						// reset the controls once we are fully in full screen
						player.setControlsSize();
					} else {
						player.isNativeFullScreen = false;
						// when a user presses ESC
						// make sure to put the player back into place
						player.exitFullScreen();
					}
				};

				if (mejs.MediaFeatures.hasMozNativeFullScreen) {
					player.globalBind(mejs.MediaFeatures.fullScreenEventName, func);
				} else {
					player.container.bind(mejs.MediaFeatures.fullScreenEventName, func);
				}
			}

			var t = this,
				normalHeight = 0,
				normalWidth = 0,
				container = player.container,
				fullscreenBtn =
					$('
' +
						'<button type="button" aria-controls="' + t.id + '" title="' + t.options.fullscreenText + '" aria-label="' + t.options.fullscreenText + '"></button>' +
					'
')
					.appendTo(controls);

				if (t.media.pluginType === 'native' || (!t.options.usePluginFullScreen && !mejs.MediaFeatures.isFirefox)) {

					fullscreenBtn.click(function() {
						var isFullScreen = (mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || player.isFullScreen;

						if (isFullScreen) {
							player.exitFullScreen();
						} else {
							player.enterFullScreen();
						}
					});

				} else {

					var hideTimeout = null,
						supportsPointerEvents = (function() {
							// TAKEN FROM MODERNIZR
							var element = document.createElement('x'),
								documentElement = document.documentElement,
								getComputedStyle = window.getComputedStyle,
								supports;
							if(!('pointerEvents' in element.style)){
								return false;
							}
							element.style.pointerEvents = 'auto';
							element.style.pointerEvents = 'x';
							documentElement.appendChild(element);
							supports = getComputedStyle &&
								getComputedStyle(element, '').pointerEvents === 'auto';
							documentElement.removeChild(element);
							return !!supports;
						})();

					//console.log('supportsPointerEvents', supportsPointerEvents);

					if (supportsPointerEvents && !mejs.MediaFeatures.isOpera) { // opera doesn't allow this :(

						// allows clicking through the fullscreen button and controls down directly to Flash

						/*
						 When a user puts his mouse over the fullscreen button, the controls are disabled
						 So we put a div over the video and another one on iether side of the fullscreen button
						 that caputre mouse movement
						 and restore the controls once the mouse moves outside of the fullscreen button
						*/

						var fullscreenIsDisabled = false,
							restoreControls = function() {
								if (fullscreenIsDisabled) {
									// hide the hovers
									for (var i in hoverDivs) {
										hoverDivs[i].hide();
									}

									// restore the control bar
									fullscreenBtn.css('pointer-events', '');
									t.controls.css('pointer-events', '');

                  // prevent clicks from pausing video
                  t.media.removeEventListener('click', t.clickToPlayPauseCallback);

									// store for later
									fullscreenIsDisabled = false;
								}
							},
							hoverDivs = {},
							hoverDivNames = ['top', 'left', 'right', 'bottom'],
							i, len,
							positionHoverDivs = function() {
								var fullScreenBtnOffsetLeft = fullscreenBtn.offset().left - t.container.offset().left,
									fullScreenBtnOffsetTop = fullscreenBtn.offset().top - t.container.offset().top,
									fullScreenBtnWidth = fullscreenBtn.outerWidth(true),
									fullScreenBtnHeight = fullscreenBtn.outerHeight(true),
									containerWidth = t.container.width(),
									containerHeight = t.container.height();

							  for (i in hoverDivs) {
									hoverDivs[i].css({position: 'absolute', top: 0, left: 0}); //, backgroundColor: '#f00'});
								}

								// over video, but not controls
								hoverDivs['top']
									.width( containerWidth )
									.height( fullScreenBtnOffsetTop );

								// over controls, but not the fullscreen button
								hoverDivs['left']
									.width( fullScreenBtnOffsetLeft )
									.height( fullScreenBtnHeight )
									.css({top: fullScreenBtnOffsetTop});

								// after the fullscreen button
								hoverDivs['right']
									.width( containerWidth - fullScreenBtnOffsetLeft - fullScreenBtnWidth )
									.height( fullScreenBtnHeight )
									.css({top: fullScreenBtnOffsetTop,
										 left: fullScreenBtnOffsetLeft + fullScreenBtnWidth});

								// under the fullscreen button
								hoverDivs['bottom']
									.width( containerWidth )
									.height( containerHeight - fullScreenBtnHeight - fullScreenBtnOffsetTop )
									.css({top: fullScreenBtnOffsetTop + fullScreenBtnHeight});
							};

						t.globalBind('resize', function() {
							positionHoverDivs();
						});

						for (i = 0, len = hoverDivNames.length; i < len; i += 1) {
							hoverDivs[hoverDivNames[i]] = $('
').appendTo(t.container).mouseover(restoreControls).hide();
						}

						// on hover, kill the fullscreen button's HTML handling, allowing clicks down to Flash
						fullscreenBtn
							.mouseover(function() {

								if (!t.isFullScreen) {

									var buttonPos = fullscreenBtn.offset(),
										containerPos = player.container.offset();

									// move the button in Flash into place
									media.positionFullscreenButton(buttonPos.left - containerPos.left, buttonPos.top - containerPos.top, false);

									// allows click through
									fullscreenBtn.css('pointer-events', 'none');
									t.controls.css('pointer-events', 'none');

                  // restore click-to-play
                  t.media.addEventListener('click', t.clickToPlayPauseCallback);

									// show the divs that will restore things
								  for (i in hoverDivs) {
										hoverDivs[i].show();
									}
									positionHoverDivs();

									fullscreenIsDisabled = true;
								}

							});

						// restore controls anytime the user enters or leaves fullscreen
						media.addEventListener('fullscreenchange', function(e) {
							t.isFullScreen = !t.isFullScreen;
							// don't allow plugin click to pause video - messes with
							// plugin's controls
              if (t.isFullScreen) {
                t.media.removeEventListener('click', t.clickToPlayPauseCallback);
              } else {
                t.media.addEventListener('click', t.clickToPlayPauseCallback);
              }
							restoreControls();
						});

						// the mouseout event doesn't work on the fullscren button, because we already killed the pointer-events
						// so we use the document.mousemove event to restore controls when the mouse moves outside the fullscreen button
						/*
						t.globalBind('mousemove', function(e) {

							// if the mouse is anywhere but the fullsceen button, then restore it all
							if (fullscreenIsDisabled) {

								var fullscreenBtnPos = fullscreenBtn.offset();

								if (e.pageY < fullscreenBtnPos.top || e.pageY > fullscreenBtnPos.top + fullscreenBtn.outerHeight(true) ||
									e.pageX < fullscreenBtnPos.left || e.pageX > fullscreenBtnPos.left + fullscreenBtn.outerWidth(true)
									) {

									fullscreenBtn.css('pointer-events', '');
									t.controls.css('pointer-events', '');

									fullscreenIsDisabled = false;
								}
							}
						});
						*/

					} else {

						// the hover state will show the fullscreen button in Flash to hover up and click

						fullscreenBtn
							.mouseover(function() {

								if (hideTimeout !== null) {
									clearTimeout(hideTimeout);
									delete hideTimeout;
								}

								var buttonPos = fullscreenBtn.offset(),
									containerPos = player.container.offset();

								media.positionFullscreenButton(buttonPos.left - containerPos.left, buttonPos.top - containerPos.top, true);

							})
							.mouseout(function() {

								if (hideTimeout !== null) {
									clearTimeout(hideTimeout);
									delete hideTimeout;
								}

								hideTimeout = setTimeout(function() {
									media.hideFullscreenButton();
								}, 1500);

							});
					}
				}

			player.fullscreenBtn = fullscreenBtn;

			t.globalBind('keydown',function (e) {
				if (((mejs.MediaFeatures.hasTrueNativeFullScreen && mejs.MediaFeatures.isFullScreen()) || t.isFullScreen) && e.keyCode == 27) {
					player.exitFullScreen();
				}
			});

		},

		cleanfullscreen: function(player) {
			player.exitFullScreen();
		},

        containerSizeTimeout: null,

		enterFullScreen: function() {

			var t = this;

			// firefox+flash can't adjust plugin sizes without resetting :(
			if (t.media.pluginType !== 'native' && (mejs.MediaFeatures.isFirefox || t.options.usePluginFullScreen)) {
				//t.media.setFullscreen(true);
				//player.isFullScreen = true;
				return;
			}

			// store overflow
			docStyleOverflow = document.documentElement.style.overflow;
			// set it to not show scroll bars so 100% will work
			document.documentElement.style.overflow = 'hidden';

			// store sizing
			normalHeight = t.container.height();
			normalWidth = t.container.width();

			// attempt to do true fullscreen (Safari 5.1 and Firefox Nightly only for now)
			if (t.media.pluginType === 'native') {
				if (mejs.MediaFeatures.hasTrueNativeFullScreen) {

					mejs.MediaFeatures.requestFullScreen(t.container[0]);
					//return;

					if (t.isInIframe) {
						// sometimes exiting from fullscreen doesn't work
						// notably in Chrome . Fixed in version 17
						setTimeout(function checkFullscreen() {

							if (t.isNativeFullScreen) {

								// check if the video is suddenly not really fullscreen
								if ($(window).width() !== screen.width) {
									// manually exit
									t.exitFullScreen();
								} else {
									// test again
									setTimeout(checkFullscreen, 500);
								}
							}

						}, 500);
					}

				} else if (mejs.MediaFeatures.hasSemiNativeFullScreen) {
					t.media.webkitEnterFullscreen();
					return;
				}
			}

			// check for iframe launch
			if (t.isInIframe) {
				var url = t.options.newWindowCallback(this);

				if (url !== '') {

					// launch immediately
					if (!mejs.MediaFeatures.hasTrueNativeFullScreen) {
						t.pause();
						window.open(url, t.id, 'top=0,left=0,width=' + screen.availWidth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no');
						return;
					} else {
						setTimeout(function() {
							if (!t.isNativeFullScreen) {
								t.pause();
								window.open(url, t.id, 'top=0,left=0,width=' + screen.availWidth + ',height=' + screen.availHeight + ',resizable=yes,scrollbars=no,status=no,toolbar=no');
							}
						}, 250);
					}
				}

			}

			// full window code

			// make full size
			t.container
				.addClass('mejs-container-fullscreen')
				.width('100%')
				.height('100%');
				//.css({position: 'fixed', left: 0, top: 0, right: 0, bottom: 0, overflow: 'hidden', width: '100%', height: '100%', 'z-index': 1000});

			// Only needed for safari 5.1 native full screen, can cause display issues elsewhere
			// Actually, it seems to be needed for IE8, too
			//if (mejs.MediaFeatures.hasTrueNativeFullScreen) {
				t.containerSizeTimeout = setTimeout(function() {
					t.container.css({width: '100%', height: '100%'});
					t.setControlsSize();
				}, 500);
			//}

			if (t.media.pluginType === 'native') {
				t.$media
					.width('100%')
					.height('100%');
			} else {
				t.container.find('.mejs-shim')
					.width('100%')
					.height('100%');

				//if (!mejs.MediaFeatures.hasTrueNativeFullScreen) {
					t.media.setVideoSize($(window).width(),$(window).height());
				//}
			}

			t.layers.children('div')
				.width('100%')
				.height('100%');

			if (t.fullscreenBtn) {
				t.fullscreenBtn
					.removeClass('mejs-fullscreen')
					.addClass('mejs-unfullscreen');
			}

			t.setControlsSize();
			t.isFullScreen = true;
		},

		exitFullScreen: function() {

			var t = this;

            // Prevent container from attempting to stretch a second time
            clearTimeout(t.containerSizeTimeout);

			// firefox can't adjust plugins
			if (t.media.pluginType !== 'native' && mejs.MediaFeatures.isFirefox) {
				t.media.setFullscreen(false);
				//player.isFullScreen = false;
				return;
			}

			// come outo of native fullscreen
			if (mejs.MediaFeatures.hasTrueNativeFullScreen && (mejs.MediaFeatures.isFullScreen() || t.isFullScreen)) {
				mejs.MediaFeatures.cancelFullScreen();
			}

			// restore scroll bars to document
			document.documentElement.style.overflow = docStyleOverflow;

			t.container
				.removeClass('mejs-container-fullscreen')
				.width(normalWidth)
				.height(normalHeight);
				//.css({position: '', left: '', top: '', right: '', bottom: '', overflow: 'inherit', width: normalWidth + 'px', height: normalHeight + 'px', 'z-index': 1});

			if (t.media.pluginType === 'native') {
				t.$media
					.width(normalWidth)
					.height(normalHeight);
			} else {
				t.container.find('.mejs-shim')
					.width(normalWidth)
					.height(normalHeight);

				t.media.setVideoSize(normalWidth, normalHeight);
			}

			t.layers.children('div')
				.width(normalWidth)
				.height(normalHeight);

			t.fullscreenBtn
				.removeClass('mejs-unfullscreen')
				.addClass('mejs-fullscreen');

			t.setControlsSize();
			t.isFullScreen = false;
		}
	});

})(mejs.$);
<|endoftext|>


<commit_before>/**
 *
 * Pseudo Tab Page Classes (JS)
 *
 * @author Takuto Yanagida @ Space-Time Inc.
 * @version 2019-03-04
 *
 */

window.ST = window['ST'] || {};

ST.addInitializer(3, function () {

	const SELECTOR_TARGET     = '.pseudo-tab-page';
	const CLS_TAB_LIST        = 'stile-pseudo-tab-page-tab-list';
	const ID_TAB_LIST_ID_BASE = '';
	const ST_STATE_CURRENT    = 'current';
	const SINGLE_TAB          = true;

	const tabUlss = [];
	const tps = document.querySelectorAll(SELECTOR_TARGET);
	for (let i = 0; i < tps.length; i += 1) {
		const tabUls = createTabPage(tps[i], i);
		if (tabUls !== false) tabUlss.push(tabUls);
	}
	if (SINGLE_TAB) initializeSingleTab();

	function createTabPage(container, idx) {
		const fh = getFirstHeading(container);
		if (!fh) return false;
		const tabH = fh.tagName;

		const cs = [].slice.call(container.children);
		const hs = [];
		const htmls = [];

		for (let i = 0; i < cs.length; i += 1) {
			const c = cs[i];
			if (c.tagName === tabH) {
				hs.push(c);
				htmls.push(c.innerHTML);
			}
		}
		const tabUls = [];
		for (let i = 0; i < hs.length; i += 1) {
			const tp = createTab(htmls, i, idx);
			container.insertBefore(tp.tabUl, hs[i]);
			tabUls.push(tp.tabUl);
		}
		if (ST.initializeAnchorOffset) ST.initializeAnchorOffset(tabUls);
		return tabUls;
	}

	function createTab(htmls, tabIdx, contIdx) {
		const tp = {};
		tp.tabUl = document.createElement('ul');
		tp.tabUl.id = ID_TAB_LIST_ID_BASE + contIdx + '-' + tabIdx;
		tp.tabUl.className = CLS_TAB_LIST;
		tp.tabUl.classList.add('stile-link-target');
		tp.tabAs = [];
		const ios = document.body.classList.contains('ios');

		for (let i = 0; i < htmls.length; i += 1) {
			const li = document.createElement('li');
			const tc = document.createElement('a');
			tc.href = '#' + ID_TAB_LIST_ID_BASE + contIdx + '-' + i;
			tc.innerHTML = htmls[i];
			tc.dataset['stile'] = ios ? 'no-anchor-scroll' : 'anchor-scroll-fast';
			if (SINGLE_TAB) tc.addEventListener('click', onTabClick);
			li.appendChild(tc);

			tp.tabUl.appendChild(li);
			tp.tabAs.push(tc);
		}
		ST.addStile(tp.tabUl.children[tabIdx], ST_STATE_CURRENT);
		return tp;
	}

	function getFirstHeading(container) {
		const cs = container.children;
		for (let i = 0; i < cs.length; i += 1) {
			const tn = cs[i].tagName;
			if (tn[0] === 'H' && (tn[1] === '1' || tn[1] === '2' || tn[1] === '3' || tn[1] === '4' || tn[1] === '5' || tn[1] === '6')) {
				return cs[i];
			}
		}
		return null;
	}

	// -------------------------------------------------------------------------

	let stopUpdateVisibility = false;

	function initializeSingleTab() {
		updateVisibility(tabUlss, true);
		let st = null;
		window.addEventListener('scroll', () => {
			if (st) clearTimeout(st);
			st = setTimeout(() => { updateVisibility(tabUlss); }, 10);
		});
	}

	function onTabClick() {
		stopUpdateVisibility = true;
		showAll(tabUlss);
		setTimeout(() => {
			stopUpdateVisibility = false;
			updateVisibility(tabUlss, true);
		}, 10);
	}

	function updateVisibility(tabUlss, immediately = false) {
		if (stopUpdateVisibility) return;
		for (let i = 0; i < tabUlss.length; i += 1) {
			const tabUls = tabUlss[i];
			let shown = false;
			for (let j = 0; j < tabUls.length; j += 1) {
				const tabUl = tabUls[j];
				const y = tabUl.getBoundingClientRect().top;

				if (immediately) ST.addStile(tabUl, 'immediately');
				if (0 < y && !shown) {
					shown = true;
					ST.removeStile(tabUl, 'hidden');
				} else if (shown) {
					ST.addStile(tabUl, 'hidden');
				}
				if (immediately) {
					setTimeout(() => { ST.removeStile(tabUl, 'immediately'); }, 1000);
				}
			}
		}
	}

	function showAll(tabUlss) {
		for (let i = 0; i < tabUlss.length; i += 1) {
			const tabUls = tabUlss[i];
			for (let j = 0; j < tabUls.length; j += 1) {
				const tabUl = tabUls[j];
				ST.addStile(tabUl, 'immediately');
				ST.removeStile(tabUl, 'hidden');
				setTimeout(() => { ST.removeStile(tabUl, 'immediately'); }, 1000);
			}
		}
	}

});
<commit_msg>Improve smoothness<commit_after>/**
 *
 * Pseudo Tab Page Classes (JS)
 *
 * @author Takuto Yanagida @ Space-Time Inc.
 * @version 2019-03-04
 *
 */

window.ST = window['ST'] || {};

ST.addInitializer(3, function () {

	const SELECTOR_TARGET     = '.pseudo-tab-page';
	const CLS_TAB_LIST        = 'stile-pseudo-tab-page-tab-list';
	const ID_TAB_LIST_ID_BASE = '';
	const ST_STATE_CURRENT    = 'current';
	const SINGLE_TAB          = true;

	const tabUlss = [];
	const tps = document.querySelectorAll(SELECTOR_TARGET);
	for (let i = 0; i < tps.length; i += 1) {
		const tabUls = createTabPage(tps[i], i);
		if (tabUls !== false) tabUlss.push(tabUls);
	}
	if (SINGLE_TAB) initializeSingleTab();

	function createTabPage(container, idx) {
		const fh = getFirstHeading(container);
		if (!fh) return false;
		const tabH = fh.tagName;

		const cs = [].slice.call(container.children);
		const hs = [];
		const htmls = [];

		for (let i = 0; i < cs.length; i += 1) {
			const c = cs[i];
			if (c.tagName === tabH) {
				hs.push(c);
				htmls.push(c.innerHTML);
			}
		}
		const tabUls = [];
		for (let i = 0; i < hs.length; i += 1) {
			const tp = createTab(htmls, i, idx, tabUls);
			container.insertBefore(tp.tabUl, hs[i]);
			tabUls.push(tp.tabUl);
		}
		if (ST.initializeAnchorOffset) ST.initializeAnchorOffset(tabUls);
		return tabUls;
	}

	function createTab(htmls, tabIdx, contIdx, tabUls) {
		const tp = {};
		tp.tabUl = document.createElement('ul');
		tp.tabUl.id = ID_TAB_LIST_ID_BASE + contIdx + '-' + tabIdx;
		tp.tabUl.className = CLS_TAB_LIST;
		tp.tabUl.classList.add('stile-link-target');
		tp.tabAs = [];
		const ios = document.body.classList.contains('ios');

		for (let i = 0; i < htmls.length; i += 1) {
			const li = document.createElement('li');
			const tc = document.createElement('a');
			tc.href = '#' + ID_TAB_LIST_ID_BASE + contIdx + '-' + i;
			tc.innerHTML = htmls[i];
			tc.dataset['stile'] = ios ? 'no-anchor-scroll' : 'anchor-scroll-fast';
			if (SINGLE_TAB) tc.addEventListener('click', () => { onTabClick(i, tabUls); });
			li.appendChild(tc);

			tp.tabUl.appendChild(li);
			tp.tabAs.push(tc);
		}
		ST.addStile(tp.tabUl.children[tabIdx], ST_STATE_CURRENT);
		if (SINGLE_TAB) ST.addStile(tp.tabUl, 'hidden');
		return tp;
	}

	function getFirstHeading(container) {
		const cs = container.children;
		for (let i = 0; i < cs.length; i += 1) {
			const tn = cs[i].tagName;
			if (tn[0] === 'H' && (tn[1] === '1' || tn[1] === '2' || tn[1] === '3' || tn[1] === '4' || tn[1] === '5' || tn[1] === '6')) {
				return cs[i];
			}
		}
		return null;
	}

	// -------------------------------------------------------------------------

	function initializeSingleTab() {
		updateVisibility(tabUlss, true);
		let st = null;
		window.addEventListener('scroll', () => {
			if (st) clearTimeout(st);
			st = setTimeout(() => { updateVisibility(tabUlss); }, 10);
		});
	}

	function onTabClick(idx, tabUls) {
		showImmediately(tabUls[idx]);
		setTimeout(() => { updateVisibility(tabUlss, true); }, 10);
	}

	function updateVisibility(tabUlss, immediately = false) {
		if (immediately) {
			for (let i = 0; i < tabUlss.length; i += 1) {
				const tabUls = tabUlss[i];
				for (let j = 0; j < tabUls.length; j += 1) {
					ST.addStile(tabUls[j], 'immediately');
				}
			}
		}
		for (let i = 0; i < tabUlss.length; i += 1) {
			const tabUls = tabUlss[i];
			let shown = false;
			for (let j = 0; j < tabUls.length; j += 1) {
				const tabUl = tabUls[j];
				const y = tabUl.getBoundingClientRect().top;
				if (0 < y && !shown) {
					shown = true;
					ST.removeStile(tabUl, 'hidden');
				} else if (shown) {
					ST.addStile(tabUl, 'hidden');
				}
			}
		}
		if (immediately) {
			setTimeout(() => {
				for (let i = 0; i < tabUlss.length; i += 1) {
					const tabUls = tabUlss[i];
					for (let j = 0; j < tabUls.length; j += 1) {
						ST.removeStile(tabUls[j], 'immediately');
					}
				}
			}, 1000);
		}
	}

	function showImmediately(tabUl) {
		ST.addStile(tabUl, 'immediately');
		ST.removeStile(tabUl, 'hidden');
		setTimeout(() => { ST.removeStile(tabUl, 'immediately'); }, 1000);
	}

});
<|endoftext|>


<commit_before>define([
  'jquery',
  '../utils',
  '../keys'
], function ($, Utils, KEYS) {
  function BaseSelection ($element, options) {
    this.$element = $element;
    this.options = options;

    BaseSelection.__super__.constructor.call(this);
  }

  Utils.Extend(BaseSelection, Utils.Observable);

  BaseSelection.prototype.render = function () {
    var $selection = $(
      '' +
      ''
    );

    this._tabindex = 0;

    if (Utils.GetData(this.$element[0], 'old-tabindex') != null) {
        this._tabindex = Utils.GetData(this.$element[0], 'old-tabindex');
    } else if (Utils.GetData(this.$element[0], 'tabindex') != null) {
        this._tabindex = Utils.GetData(this.$element[0], 'tabindex');
    }

    $selection.attr('title', this.$element.attr('title'));
    $selection.attr('tabindex', this._tabindex);

    this.$selection = $selection;

    return $selection;
  };

  BaseSelection.prototype.bind = function (container, $container) {
    var self = this;

    var id = container.id + '-container';
    var resultsId = container.id + '-results';

    this.container = container;

    this.$selection.on('focus', function (evt) {
      self.trigger('focus', evt);
    });

    this.$selection.on('blur', function (evt) {
      self._handleBlur(evt);
    });

    this.$selection.on('keydown', function (evt) {
      self.trigger('keypress', evt);

      if (evt.which === KEYS.SPACE) {
        evt.preventDefault();
      }
    });

    container.on('results:focus', function (params) {
      self.$selection.attr('aria-activedescendant', params.data._resultId);
    });

    container.on('selection:update', function (params) {
      self.update(params.data);
    });

    container.on('open', function () {
      // When the dropdown is open, aria-expanded="true"
      self.$selection.attr('aria-expanded', 'true');
      self.$selection.attr('aria-owns', resultsId);

      self._attachCloseHandler(container);
    });

    container.on('close', function () {
      // When the dropdown is closed, aria-expanded="false"
      self.$selection.attr('aria-expanded', 'false');
      self.$selection.removeAttr('aria-activedescendant');
      self.$selection.removeAttr('aria-owns');

      self.$selection.focus();

      self._detachCloseHandler(container);
    });

    container.on('enable', function () {
      self.$selection.attr('tabindex', self._tabindex);
    });

    container.on('disable', function () {
      self.$selection.attr('tabindex', '-1');
    });
  };

  BaseSelection.prototype._handleBlur = function (evt) {
    var self = this;

    // This needs to be delayed as the active element is the body when the tab
    // key is pressed, possibly along with others.
    window.setTimeout(function () {
      // Don't trigger `blur` if the focus is still in the selection
      if (
        (document.activeElement == self.$selection[0]) ||
        ($.contains(self.$selection[0], document.activeElement))
      ) {
        return;
      }

      self.trigger('blur', evt);
    }, 1);
  };

  BaseSelection.prototype._attachCloseHandler = function (container) {
    var self = this;

    $(document.body).on('mousedown.select2.' + container.id, function (e) {
      var $target = $(e.target);

      var $select = $target.closest('.select2');

      var $all = $('.select2.select2-container--open');

      $all.each(function () {
        var $this = $(this);

        if (this == $select[0]) {
          return;
        }

        var $element = Utils.GetData(this, 'element');

        $element.select2('close');
      });
    });
  };

  BaseSelection.prototype._detachCloseHandler = function (container) {
    $(document.body).off('mousedown.select2.' + container.id);
  };

  BaseSelection.prototype.position = function ($selection, $container) {
    var $selectionContainer = $container.find('.selection');
    $selectionContainer.append($selection);
  };

  BaseSelection.prototype.destroy = function () {
    this._detachCloseHandler(this.container);
  };

  BaseSelection.prototype.update = function (data) {
    throw new Error('The `update` method must be defined in child classes.');
  };

  return BaseSelection;
});
<commit_msg>Reverted a line in base.js regarding tab index.<commit_after>define([
  'jquery',
  '../utils',
  '../keys'
], function ($, Utils, KEYS) {
  function BaseSelection ($element, options) {
    this.$element = $element;
    this.options = options;

    BaseSelection.__super__.constructor.call(this);
  }

  Utils.Extend(BaseSelection, Utils.Observable);

  BaseSelection.prototype.render = function () {
    var $selection = $(
      '' +
      ''
    );

    this._tabindex = 0;

    if (Utils.GetData(this.$element[0], 'old-tabindex') != null) {
        this._tabindex = Utils.GetData(this.$element[0], 'old-tabindex');
    } else if (this.$element.attr('tabindex') != null) {
        this._tabindex = this.$element.attr('tabindex');
    }

    $selection.attr('title', this.$element.attr('title'));
    $selection.attr('tabindex', this._tabindex);

    this.$selection = $selection;

    return $selection;
  };

  BaseSelection.prototype.bind = function (container, $container) {
    var self = this;

    var id = container.id + '-container';
    var resultsId = container.id + '-results';

    this.container = container;

    this.$selection.on('focus', function (evt) {
      self.trigger('focus', evt);
    });

    this.$selection.on('blur', function (evt) {
      self._handleBlur(evt);
    });

    this.$selection.on('keydown', function (evt) {
      self.trigger('keypress', evt);

      if (evt.which === KEYS.SPACE) {
        evt.preventDefault();
      }
    });

    container.on('results:focus', function (params) {
      self.$selection.attr('aria-activedescendant', params.data._resultId);
    });

    container.on('selection:update', function (params) {
      self.update(params.data);
    });

    container.on('open', function () {
      // When the dropdown is open, aria-expanded="true"
      self.$selection.attr('aria-expanded', 'true');
      self.$selection.attr('aria-owns', resultsId);

      self._attachCloseHandler(container);
    });

    container.on('close', function () {
      // When the dropdown is closed, aria-expanded="false"
      self.$selection.attr('aria-expanded', 'false');
      self.$selection.removeAttr('aria-activedescendant');
      self.$selection.removeAttr('aria-owns');

      self.$selection.focus();

      self._detachCloseHandler(container);
    });

    container.on('enable', function () {
      self.$selection.attr('tabindex', self._tabindex);
    });

    container.on('disable', function () {
      self.$selection.attr('tabindex', '-1');
    });
  };

  BaseSelection.prototype._handleBlur = function (evt) {
    var self = this;

    // This needs to be delayed as the active element is the body when the tab
    // key is pressed, possibly along with others.
    window.setTimeout(function () {
      // Don't trigger `blur` if the focus is still in the selection
      if (
        (document.activeElement == self.$selection[0]) ||
        ($.contains(self.$selection[0], document.activeElement))
      ) {
        return;
      }

      self.trigger('blur', evt);
    }, 1);
  };

  BaseSelection.prototype._attachCloseHandler = function (container) {
    var self = this;

    $(document.body).on('mousedown.select2.' + container.id, function (e) {
      var $target = $(e.target);

      var $select = $target.closest('.select2');

      var $all = $('.select2.select2-container--open');

      $all.each(function () {
        var $this = $(this);

        if (this == $select[0]) {
          return;
        }

        var $element = Utils.GetData(this, 'element');

        $element.select2('close');
      });
    });
  };

  BaseSelection.prototype._detachCloseHandler = function (container) {
    $(document.body).off('mousedown.select2.' + container.id);
  };

  BaseSelection.prototype.position = function ($selection, $container) {
    var $selectionContainer = $container.find('.selection');
    $selectionContainer.append($selection);
  };

  BaseSelection.prototype.destroy = function () {
    this._detachCloseHandler(this.container);
  };

  BaseSelection.prototype.update = function (data) {
    throw new Error('The `update` method must be defined in child classes.');
  };

  return BaseSelection;
});
<|endoftext|>


<commit_before>'use strict';

angular.module('copayApp.services').factory('configService', function(storageService, lodash, $log, isCordova) {
  var root = {};

	root.colorOpts = [
	  '#DD4B39',
	  '#F38F12',
	  '#FAA77F',
	  '#FADA58',
	  '#9EDD72',
	  '#77DADA',
	  '#4A90E2',
	  '#484ED3',
	  '#9B59B6',
	  '#E856EF',
	  '#FF599E',
	  '#7A8C9E',
	];

  var constants = require('ocore/constants.js');
  var isTestnet = constants.version.match(/t$/);
  root.TIMESTAMPER_ADDRESS = isTestnet ? 'OPNUXBRSSQQGHKQNEPD2GLWQYEUY5XLD' : 'I2ADHGP4HL6J37NQAD73J7E5SKFIXJOT';

  root.oracles = {
		"FOPUBEUPBC6YLIQDLKL6EW775BMV7YOH": {
			name: "Bitcoin oracle",
			feedname_placeholder: "bitcoin_merkle or randomXXXXXX",
			feedvalue_placeholder: "e.g. 1LR5xew1X13okNYKRu7qA3uN4hpRH1Tfnn:0.5",
			instructions_url: "https://medium.com/obyte/making-p2p-great-again-episode-ii-bitcoin-exchange-d98adfbde2a5",
			feednames_filter: ["^bitcoin_merkle$", "^random[\\d]+$"],
			feedvalues_filter: ["^[13][a-km-zA-HJ-NP-Z1-9]{25,34}\\:[0-9\\.]+$", "^\\d{1,6}$"]
		},
		"JPQKPRI5FMTQRJF4ZZMYZYDQVRD55OTC" : {
			name: "Crypto exchange rates oracle",
			feedname_placeholder: "e.g. BTC_USD",
			feedvalue_placeholder: "e.g. 1234.56",
			instructions_url: "https://wiki.obyte.org/Oracle#Using_the_crypto-exchange-rates_oracle_in_a_smart_contract",
			feednames_filter: ["^[\\dA-Z]+_[\\dA-Z]+$"],
			feedvalues_filter: ["^[\\d\\.]+$"]
		},
		"GFK3RDAPQLLNCMQEVGGD2KCPZTLSG3HN" : {
			name: "Flight delay oracle",
			feedname_placeholder: "e.g. BA950-2018-12-25",
			feedvalue_placeholder: "e.g. 30",
			instructions_url: "https://wiki.obyte.org/Oracle#Flight_delays_tracker",
			feednames_filter: ["^[\\w\\d]+-\\d{4}-\\d{2}-\\d{2}$"],
			feedvalues_filter: ["^[\\d]+$"]
		},
		"TKT4UESIKTTRALRRLWS4SENSTJX6ODCW" : {
			name: "Sports betting oracle",
			feedname_placeholder: "e.g. BROOKLYNNETS_CHARLOTTEHORNETS_2018-03-21",
			feedvalue_placeholder: "e.g. BROOKLYNNETS",
			instructions_url: "https://wiki.obyte.org/Sports_betting",
			feednames_filter: ["^[\\w\\d]+_[\\w\\d]+_\\d{4}-\\d{2}-\\d{2}$"],
			feedvalues_filter: ["^[\\w\\d]+$"]
		},
		"I2ADHGP4HL6J37NQAD73J7E5SKFIXJOT" : {
			name: "Timestamp oracle",
			feedname_placeholder: "timestamp",
			feedvalue_placeholder: "e.g. 1541341626704",
			instructions_url: "https://wiki.obyte.org/Oracle",
			feednames_filter: ["^timestamp$"],
			feedvalues_filter: ["^\\d{13,}$"]
		}
	};

	root.privateTextcoinExt = 'coin';

  var defaultConfig = {
	// wallet limits
	limits: {
		totalCosigners: 6
	},

	hub: (constants.alt === '2' && isTestnet) ? 'obyte.org/bb-test' : 'obyte.org/bb',
	attestorAddresses: {
		email: 'H5EZTQE7ABFH27AUDTQFMZIALANK6RBG',
		reddit: 'OYW2XTDKSNKGSEZ27LMGNOPJSYIXHBHC',
		steem: 'JEDZYC2HMGDBIDQKG3XSTXUSHMCBK725',
		username: 'UENJPVZ7HVHM6QGVGT6MWOJGGRTUTJXQ'
	},
	realNameAttestorAddresses: [{address: 'I2ADHGP4HL6J37NQAD73J7E5SKFIXJOT', name: 'Real name attestation bot'}],

	// requires bluetooth permission on android
	//deviceName: /*isCordova ? cordova.plugins.deviceName.name : */require('os').hostname(),

	getDeviceName: function(){
		return isCordova ? cordova.plugins.deviceName.name : require('os').hostname();
	},

	// wallet default config
	wallet: {
	  requiredCosigners: 2,
	  totalCosigners: 3,
	  spendUnconfirmed: false,
	  reconnectDelay: 5000,
	  idleDurationMin: 4,
	  singleAddress: false,
	  settings: {
		unitName: 'bytes',
		unitValue: 1,
		unitDecimals: 0,
		unitCode: 'one',
		bbUnitName: 'blackbytes',
		bbUnitValue: 1,
		bbUnitDecimals: 0,
		bbUnitCode: 'one',
		alternativeName: 'US Dollar',
		alternativeIsoCode: 'USD',
		},
	},

	// hidden assets: key = wallet id, value = set of assets (string: boolean)
	hiddenAssets: {},

	rates: {
	  url: 'https://insight.bitpay.com:443/api/rates',
	},

	pushNotifications: {
	  enabled: true,
	  config: {
		android: {
		  icon: 'push',
		  iconColor: '#2F4053'
		},
		ios: {
		  alert: 'true',
		  badge: 'true',
		  sound: 'true',
		},
		windows: {},
	  }
	},

  autoUpdateWitnessesList: true
  };

  var configCache = null;

  root.getSync = function() {
	if (!configCache)
		throw new Error('configService#getSync called when cache is not initialized');
	return configCache;
  };

  root.get = function(cb) {

	storageService.getConfig(function(err, localConfig) {
	  configCache = migrateLocalConfig(localConfig);
	  $log.debug('Preferences read:', configCache);
	  return cb(err, configCache);
	});
  };

  root.set = function(newOpts, cb) {
	var config = defaultConfig;
	storageService.getConfig(function(err, oldOpts) {
	  if (lodash.isString(oldOpts)) {
		oldOpts = JSON.parse(oldOpts);
	  }
	  if (lodash.isString(config)) {
		config = JSON.parse(config);
	  }
	  if (lodash.isString(newOpts)) {
		newOpts = JSON.parse(newOpts);
	  }
	  lodash.merge(config, oldOpts, newOpts);
	  if (newOpts.realNameAttestorAddresses)
	  	config.realNameAttestorAddresses = newOpts.realNameAttestorAddresses;
	  checkAndReplaceOldUnitCode(config.wallet.settings);
	  configCache = config;

	  storageService.storeConfig(JSON.stringify(config), cb);
	});
  };

  root.reset = function(cb) {
	configCache = lodash.clone(defaultConfig);
	storageService.removeConfig(cb);
  };

  root.getDefaults = function() {
	return lodash.clone(defaultConfig);
  };

  if(window.config){
	configCache = migrateLocalConfig(window.config);
  }else{
	root.get(function() {});
  }

  function migrateLocalConfig(localConfig) {
	if (localConfig) {
		var _config = JSON.parse(localConfig);

		//these ifs are to avoid migration problems
		if (!_config.wallet) {
			_config.wallet = defaultConfig.wallet;
		}
		if (!_config.wallet.settings.unitCode) {
			_config.wallet.settings.unitCode = defaultConfig.wallet.settings.unitCode;
		}
		if (!_config.wallet.settings.unitValue){
			if(_config.wallet.settings.unitToBytes){
				_config.wallet.settings.unitValue = _config.wallet.settings.unitToBytes;
			}else{
				_config.wallet.settings.unitValue = defaultConfig.wallet.settings.unitValue;
			}
		}
		if (!_config.wallet.settings.bbUnitName) {
			_config.wallet.settings.bbUnitName = defaultConfig.wallet.settings.bbUnitName;
		}
		if (!_config.wallet.settings.bbUnitValue) {
			_config.wallet.settings.bbUnitValue = defaultConfig.wallet.settings.bbUnitValue;
		}
		if (!_config.wallet.settings.bbUnitDecimals) {
			_config.wallet.settings.bbUnitDecimals = defaultConfig.wallet.settings.bbUnitDecimals;
		}
		if (!_config.wallet.settings.bbUnitCode) {
			_config.wallet.settings.bbUnitCode = defaultConfig.wallet.settings.bbUnitCode;
		}
		if (!_config.pushNotifications) {
			_config.pushNotifications = defaultConfig.pushNotifications;
		}
		if (!_config.hub)
			_config.hub = defaultConfig.hub;
		if (!_config.attestorAddresses) {
			_config.attestorAddresses = defaultConfig.attestorAddresses;
		}
		if (!_config.realNameAttestorAddresses) {
			_config.realNameAttestorAddresses = defaultConfig.realNameAttestorAddresses;
		}
		for (var attestorKey in defaultConfig.attestorAddresses){
			if (!(attestorKey in _config.attestorAddresses))
				_config.attestorAddresses[attestorKey] = defaultConfig.attestorAddresses[attestorKey];
		}
		if (!_config.hiddenAssets) {
			_config.hiddenAssets = defaultConfig.hiddenAssets;
		}
		if (!_config.deviceName)
			_config.deviceName = defaultConfig.getDeviceName();

		checkAndReplaceOldUnitCode(_config.wallet.settings);
	} else {
		_config = lodash.clone(defaultConfig);
		_config.deviceName = defaultConfig.getDeviceName();
	}
	return _config;
  }

  function checkAndReplaceOldUnitCode(setting) {
	  switch (setting.unitCode){
		  case 'byte':
				setting.unitCode = 'one';
				setting.unitValue = 1;
		  	break;
		  case 'kB':
				setting.unitCode = 'kilo';
				setting.unitValue = 1000;
		  	break;
		  case 'MB':
				setting.unitCode = 'mega';
				setting.unitValue = 1000000;
		  	break;
		  case 'GB':
				setting.unitCode = 'giga';
				setting.unitValue = 1000000000;
		  	break;
	  }
  }

  return root;
});
<commit_msg>estonian real name attestor<commit_after>'use strict';

angular.module('copayApp.services').factory('configService', function(storageService, lodash, $log, isCordova) {
  var root = {};

	root.colorOpts = [
	  '#DD4B39',
	  '#F38F12',
	  '#FAA77F',
	  '#FADA58',
	  '#9EDD72',
	  '#77DADA',
	  '#4A90E2',
	  '#484ED3',
	  '#9B59B6',
	  '#E856EF',
	  '#FF599E',
	  '#7A8C9E',
	];

  var constants = require('ocore/constants.js');
  var isTestnet = constants.version.match(/t$/);
  root.TIMESTAMPER_ADDRESS = isTestnet ? 'OPNUXBRSSQQGHKQNEPD2GLWQYEUY5XLD' : 'I2ADHGP4HL6J37NQAD73J7E5SKFIXJOT';

  root.oracles = {
		"FOPUBEUPBC6YLIQDLKL6EW775BMV7YOH": {
			name: "Bitcoin oracle",
			feedname_placeholder: "bitcoin_merkle or randomXXXXXX",
			feedvalue_placeholder: "e.g. 1LR5xew1X13okNYKRu7qA3uN4hpRH1Tfnn:0.5",
			instructions_url: "https://medium.com/obyte/making-p2p-great-again-episode-ii-bitcoin-exchange-d98adfbde2a5",
			feednames_filter: ["^bitcoin_merkle$", "^random[\\d]+$"],
			feedvalues_filter: ["^[13][a-km-zA-HJ-NP-Z1-9]{25,34}\\:[0-9\\.]+$", "^\\d{1,6}$"]
		},
		"JPQKPRI5FMTQRJF4ZZMYZYDQVRD55OTC" : {
			name: "Crypto exchange rates oracle",
			feedname_placeholder: "e.g. BTC_USD",
			feedvalue_placeholder: "e.g. 1234.56",
			instructions_url: "https://wiki.obyte.org/Oracle#Using_the_crypto-exchange-rates_oracle_in_a_smart_contract",
			feednames_filter: ["^[\\dA-Z]+_[\\dA-Z]+$"],
			feedvalues_filter: ["^[\\d\\.]+$"]
		},
		"GFK3RDAPQLLNCMQEVGGD2KCPZTLSG3HN" : {
			name: "Flight delay oracle",
			feedname_placeholder: "e.g. BA950-2018-12-25",
			feedvalue_placeholder: "e.g. 30",
			instructions_url: "https://wiki.obyte.org/Oracle#Flight_delays_tracker",
			feednames_filter: ["^[\\w\\d]+-\\d{4}-\\d{2}-\\d{2}$"],
			feedvalues_filter: ["^[\\d]+$"]
		},
		"TKT4UESIKTTRALRRLWS4SENSTJX6ODCW" : {
			name: "Sports betting oracle",
			feedname_placeholder: "e.g. BROOKLYNNETS_CHARLOTTEHORNETS_2018-03-21",
			feedvalue_placeholder: "e.g. BROOKLYNNETS",
			instructions_url: "https://wiki.obyte.org/Sports_betting",
			feednames_filter: ["^[\\w\\d]+_[\\w\\d]+_\\d{4}-\\d{2}-\\d{2}$"],
			feedvalues_filter: ["^[\\w\\d]+$"]
		},
		"I2ADHGP4HL6J37NQAD73J7E5SKFIXJOT" : {
			name: "Timestamp oracle",
			feedname_placeholder: "timestamp",
			feedvalue_placeholder: "e.g. 1541341626704",
			instructions_url: "https://wiki.obyte.org/Oracle",
			feednames_filter: ["^timestamp$"],
			feedvalues_filter: ["^\\d{13,}$"]
		}
	};

	root.privateTextcoinExt = 'coin';

  var defaultConfig = {
	// wallet limits
	limits: {
		totalCosigners: 6
	},

	hub: (constants.alt === '2' && isTestnet) ? 'obyte.org/bb-test' : 'obyte.org/bb',
	attestorAddresses: {
		email: 'H5EZTQE7ABFH27AUDTQFMZIALANK6RBG',
		reddit: 'OYW2XTDKSNKGSEZ27LMGNOPJSYIXHBHC',
		steem: 'JEDZYC2HMGDBIDQKG3XSTXUSHMCBK725',
		username: 'UENJPVZ7HVHM6QGVGT6MWOJGGRTUTJXQ'
	},
	realNameAttestorAddresses: [
		{ address: 'I2ADHGP4HL6J37NQAD73J7E5SKFIXJOT', name: 'Real name attestation bot (Jumio)' },
		{ address: 'OHVQ2R5B6TUR5U7WJNYLP3FIOSR7VCED', name: 'Real name attestation bot (Smart card, Mobile ID, Smart ID)' }
	],

	// requires bluetooth permission on android
	//deviceName: /*isCordova ? cordova.plugins.deviceName.name : */require('os').hostname(),

	getDeviceName: function(){
		return isCordova ? cordova.plugins.deviceName.name : require('os').hostname();
	},

	// wallet default config
	wallet: {
	  requiredCosigners: 2,
	  totalCosigners: 3,
	  spendUnconfirmed: false,
	  reconnectDelay: 5000,
	  idleDurationMin: 4,
	  singleAddress: false,
	  settings: {
		unitName: 'bytes',
		unitValue: 1,
		unitDecimals: 0,
		unitCode: 'one',
		bbUnitName: 'blackbytes',
		bbUnitValue: 1,
		bbUnitDecimals: 0,
		bbUnitCode: 'one',
		alternativeName: 'US Dollar',
		alternativeIsoCode: 'USD',
		},
	},

	// hidden assets: key = wallet id, value = set of assets (string: boolean)
	hiddenAssets: {},

	rates: {
	  url: 'https://insight.bitpay.com:443/api/rates',
	},

	pushNotifications: {
	  enabled: true,
	  config: {
		android: {
		  icon: 'push',
		  iconColor: '#2F4053'
		},
		ios: {
		  alert: 'true',
		  badge: 'true',
		  sound: 'true',
		},
		windows: {},
	  }
	},

  autoUpdateWitnessesList: true
  };

  var configCache = null;

  root.getSync = function() {
	if (!configCache)
		throw new Error('configService#getSync called when cache is not initialized');
	return configCache;
  };

  root.get = function(cb) {

	storageService.getConfig(function(err, localConfig) {
	  configCache = migrateLocalConfig(localConfig);
	  $log.debug('Preferences read:', configCache);
	  return cb(err, configCache);
	});
  };

  root.set = function(newOpts, cb) {
	var config = defaultConfig;
	storageService.getConfig(function(err, oldOpts) {
	  if (lodash.isString(oldOpts)) {
		oldOpts = JSON.parse(oldOpts);
	  }
	  if (lodash.isString(config)) {
		config = JSON.parse(config);
	  }
	  if (lodash.isString(newOpts)) {
		newOpts = JSON.parse(newOpts);
	  }
	  lodash.merge(config, oldOpts, newOpts);
	  if (newOpts.realNameAttestorAddresses)
	  	config.realNameAttestorAddresses = newOpts.realNameAttestorAddresses;
	  checkAndReplaceOldUnitCode(config.wallet.settings);
	  configCache = config;

	  storageService.storeConfig(JSON.stringify(config), cb);
	});
  };

  root.reset = function(cb) {
	configCache = lodash.clone(defaultConfig);
	storageService.removeConfig(cb);
  };

  root.getDefaults = function() {
	return lodash.clone(defaultConfig);
  };

  if(window.config){
	configCache = migrateLocalConfig(window.config);
  }else{
	root.get(function() {});
  }

  function migrateLocalConfig(localConfig) {
	if (localConfig) {
		var _config = JSON.parse(localConfig);

		//these ifs are to avoid migration problems
		if (!_config.wallet) {
			_config.wallet = defaultConfig.wallet;
		}
		if (!_config.wallet.settings.unitCode) {
			_config.wallet.settings.unitCode = defaultConfig.wallet.settings.unitCode;
		}
		if (!_config.wallet.settings.unitValue){
			if(_config.wallet.settings.unitToBytes){
				_config.wallet.settings.unitValue = _config.wallet.settings.unitToBytes;
			}else{
				_config.wallet.settings.unitValue = defaultConfig.wallet.settings.unitValue;
			}
		}
		if (!_config.wallet.settings.bbUnitName) {
			_config.wallet.settings.bbUnitName = defaultConfig.wallet.settings.bbUnitName;
		}
		if (!_config.wallet.settings.bbUnitValue) {
			_config.wallet.settings.bbUnitValue = defaultConfig.wallet.settings.bbUnitValue;
		}
		if (!_config.wallet.settings.bbUnitDecimals) {
			_config.wallet.settings.bbUnitDecimals = defaultConfig.wallet.settings.bbUnitDecimals;
		}
		if (!_config.wallet.settings.bbUnitCode) {
			_config.wallet.settings.bbUnitCode = defaultConfig.wallet.settings.bbUnitCode;
		}
		if (!_config.pushNotifications) {
			_config.pushNotifications = defaultConfig.pushNotifications;
		}
		if (!_config.hub)
			_config.hub = defaultConfig.hub;
		if (!_config.attestorAddresses) {
			_config.attestorAddresses = defaultConfig.attestorAddresses;
		}
		if (!_config.realNameAttestorAddresses) {
			_config.realNameAttestorAddresses = defaultConfig.realNameAttestorAddresses;
		}
		for (var attestorKey in defaultConfig.attestorAddresses){
			if (!(attestorKey in _config.attestorAddresses))
				_config.attestorAddresses[attestorKey] = defaultConfig.attestorAddresses[attestorKey];
		}
		if (!_config.hiddenAssets) {
			_config.hiddenAssets = defaultConfig.hiddenAssets;
		}
		if (!_config.deviceName)
			_config.deviceName = defaultConfig.getDeviceName();

		checkAndReplaceOldUnitCode(_config.wallet.settings);
	} else {
		_config = lodash.clone(defaultConfig);
		_config.deviceName = defaultConfig.getDeviceName();
	}
	return _config;
  }

  function checkAndReplaceOldUnitCode(setting) {
	  switch (setting.unitCode){
		  case 'byte':
				setting.unitCode = 'one';
				setting.unitValue = 1;
		  	break;
		  case 'kB':
				setting.unitCode = 'kilo';
				setting.unitValue = 1000;
		  	break;
		  case 'MB':
				setting.unitCode = 'mega';
				setting.unitValue = 1000000;
		  	break;
		  case 'GB':
				setting.unitCode = 'giga';
				setting.unitValue = 1000000000;
		  	break;
	  }
  }

  return root;
});
<|endoftext|>


<commit_before>//* globals WallabagApi */

// declarations

if (typeof (browser) === 'undefined' && typeof (chrome) === 'object') {
    browser = chrome;
}

let Port = null;
let portConnected = false;

var CacheType = function (enable) {
    this.enabled = enable;
    this._cache = [];
};

CacheType.prototype = {
    _cache: null,
    enabled: null,

    str: function (some) {
        return btoa(JSON.stringify(some));
    },

    set: function (key, data) {
        if (this.enabled) {
            this._cache[this.str(key)] = data;
        }
    },

    clear: function (key) {
        if (this.enabled) {
            delete this._cache[this.str(key)];
        }
    },

    check: function (key) {
        return this.enabled && (this._cache[this.str(key)] !== undefined);
    },

    get: function (key) {
        return this.enabled ? this._cache[this.str(key)] : undefined;
    }
};

const icons = {
    'default': browser.runtime.getManifest().browser_action.default_icon,
    'good': 'img/wallabagger-green.svg',
    'wip': 'img/wallabagger-yellow.svg',
    'bad': 'img/wallabagger-red.svg'

};

const wallabagContextMenus = [
    {
        id: 'wallabagger-add-link',
        title: 'Wallabag it!',
        contexts: ['link', 'page']
    },
    {
        type: 'separator',
        contexts: ['browser_action']
    },
    {
        id: 'unread',
        title: 'Unread articles',
        contexts: ['browser_action']
    },
    {
        id: 'starred',
        title: 'Starred articles',
        contexts: ['browser_action']
    },
    {
        id: 'archive',
        title: 'Archived articles',
        contexts: ['browser_action']
    },
    {
        id: 'all',
        title: 'All articles',
        contexts: ['browser_action']
    },
    {
        id: 'tag',
        title: 'Tag list',
        contexts: ['browser_action']
    }
];

const existStates = {
    exists: 'exists',
    notexists: 'notexists',
    wip: 'wip'
};

const cache = new CacheType(true); // TODO - here checking option
const dirtyCache = new CacheType(true);
const existCache = new CacheType(true);

const api = new WallabagApi();

// Code

api.init().then(data => {
    addExistCheckListeners(api.data.AllowExistCheck);
    api.GetTags().then(tags => { cache.set('allTags', tags); });
});

addListeners();
createContextMenus();

// Functions
function createContextMenus () {
    wallabagContextMenus.map(menu => { browser.contextMenus.create(menu); });
}

function onTabActivatedListener (activeInfo) {
    setIcon(icons.default);
    const { tabId } = activeInfo;
    browser.tabs.get(tabId, function (tab) {
        checkExist(tab.url);
    });
}

function onTabCreatedListener (tab) {
    setIcon(icons.default);
}

function onTabUpdatedListener (tabId, changeInfo, tab) {
    if (changeInfo.status === 'loading' && tab.active) {
        saveExistFlag(tab.url, existStates.notexists);
        requestExists(tab.url);
    }
}

function addExistCheckListeners (enable) {
    if (enable) {
        browser.tabs.onActivated.addListener(onTabActivatedListener);
        browser.tabs.onCreated.addListener(onTabCreatedListener);
        browser.tabs.onUpdated.addListener(onTabUpdatedListener);
    } else {
        if (browser.tab && browser.tab.onActivated.hasListener(onTabActivatedListener)) {
            browser.tab.onActivated.removeListener(onTabActivatedListener);
        }
        if (browser.tab && browser.tabs.onCreated.hasListener(onTabCreatedListener)) {
            browser.tabs.onCreated.removeListener(onTabCreatedListener);
        }
        if (browser.tab && browser.tabs.onUpdated.hasListener(onTabUpdatedListener)) {
            browser.tabs.onUpdated.remoneListener(onTabUpdatedListener);
        }
    }
}

function onContextMenusClicked (info) {
    switch (info.menuItemId) {
        case 'wallabagger-add-link':
            if (info.linkUrl.length > 0) {
                savePageToWallabag(info.linkUrl, true);
            } else {
                savePageToWallabag(info.pageUrl, false);
            }
            break;
        case 'unread':
        case 'starred':
        case 'archive':
        case 'all':
        case 'tag':
            GotoWallabag(info.menuItemId);
            break;
    }
}

function onCommandsCommand (command) {
    if (command === 'wallabag-it') {
        browser.tabs.query({ 'active': true, 'currentWindow': true }, function (tabs) {
            if (tabs[0] != null) {
                savePageToWallabag(tabs[0].url, false);
            }
        });
    }
}

function postIfConnected (obj) {
    portConnected && Port.postMessage(obj);
}
function onPortMessage (msg) {
    try {
        switch (msg.request) {
            case 'save':
                savePageToWallabag(msg.tabUrl);
                break;
            case 'tags':
                if (!cache.check('allTags')) {
                    api.GetTags()
                            .then(data => {
                                postIfConnected({ response: 'tags', tags: data });
                                cache.set('allTags', data);
                            });
                } else {
                    postIfConnected({ response: 'tags', tags: cache.get('allTags') });
                }
                break;
            case 'saveTitle':
                if (msg.articleId !== -1) {
                    api.SaveTitle(msg.articleId, msg.title).then(data => {
                        postIfConnected({ response: 'title', title: data.title });
                        cache.set(msg.tabUrl, data);
                    });
                } else {
                    dirtyCacheSet(msg.tabUrl, {title: msg.title});
                }
                break;
            case 'deleteArticle':
                if (msg.articleId !== -1) {
                    api.DeleteArticle(msg.articleId).then(data => {
                        cache.clear(msg.tabUrl);
                    });
                } else {
                    dirtyCacheSet(msg.tabUrl, {deleted: true});
                }
                setIcon(icons.default);
                saveExistFlag(msg.tabUrl, existStates.notexists);
                break;
            case 'setup':
                postIfConnected({ response: 'setup', data: api.data });
                break;
            case 'setup-save':
                api.setsave(msg.data);
                postIfConnected({ response: 'setup-save', data: api.data });
                addExistCheckListeners(msg.data.AllowExistCheck);
                break;
            case 'setup-gettoken':
                api.setsave(msg.data);
                api.GetAppToken()
                        .then(a => {
                            api.save();
                            postIfConnected({ response: 'setup-gettoken', data: api.data, result: true });
                            if (!cache.check('allTags')) {
                                api.GetTags()
                                .then(data => { cache.set('allTags', data); });
                            }
                        })
                        .catch(a => {
                            postIfConnected({ response: 'setup-gettoken', data: api.data, result: false });
                        });
                break;
            case 'setup-checkurl':
                api.setsave(msg.data);
                api.CheckUrl()
                        .then(a => {
                            postIfConnected({ response: 'setup-checkurl', data: api.data, result: true });
                        })
                        .catch(a => {
                            api.clear();
                            // api.save();
                            postIfConnected({ response: 'setup-checkurl', data: api.data, result: false });
                        });
                break;
            case 'deleteArticleTag':
                if (msg.articleId !== -1) {
                    api.DeleteArticleTag(msg.articleId, msg.tagId).then(data => {
                        postIfConnected({ response: 'articleTags', tags: data.tags });
                        cache.set(msg.tabUrl, data);
                    });
                } else {
                    dirtyCacheSet(msg.tabUrl, {tagList: msg.tags});
                }
                break;
            case 'saveTags':
                if (msg.articleId !== -1) {
                    api.SaveTags(msg.articleId, msg.tags).then(data => {
                        postIfConnected({ response: 'articleTags', tags: data.tags });
                        cache.set(msg.tabUrl, data);
                    });
                } else {
                    dirtyCacheSet(msg.tabUrl, {tagList: msg.tags});
                }
                break;
            case 'SaveStarred':
            case 'SaveArchived':
                if (msg.articleId !== -1) {
                    api[msg.request](msg.articleId, msg.value ? 1 : 0).then(data => {
                        postIfConnected({ response: 'action', value: {starred: data.is_starred === 1, archived: data.is_archived === 1} });
                        cache.set(msg.tabUrl, data);
                    });
                } else {
                    dirtyCacheSet(msg.tabUrl, (msg.request === 'SaveStarred') ? {is_starred: msg.value ? 1 : 0} : {is_archived: msg.value ? 1 : 0});
                }
                break;
            default: {
                console.log(`unknown request ${JSON.stringify(msg)}`);
            }
        }
    } catch (error) {
        setIcon(icons.bad);
        setTimeout(function () { setIcon(icons.default); }, 5000);
        postIfConnected({ response: 'error', error: error });
    }
}

function onRuntimeConnect (port) {
    Port = port;
    portConnected = true;

    Port.onDisconnect.addListener(function () { portConnected = false; });
    Port.onMessage.addListener(onPortMessage);
}

function addListeners () {
    browser.contextMenus.onClicked.addListener(onContextMenusClicked);
    browser.commands.onCommand.addListener(onCommandsCommand);
    browser.runtime.onConnect.addListener(onRuntimeConnect);
}

function setIcon (icon) {
    browser.browserAction.setIcon({ path: icon });
}

function dirtyCacheSet (key, obj) {
    dirtyCache.set(key, Object.assign(dirtyCache.check(key) ? dirtyCache.get(key) : {}, obj));
    dirtyCache.set(key, Object.assign(dirtyCache.check(key) ? dirtyCache.get(key) : {}, { id: -1, url: key }));
}

function applyDirtyCacheLight (key, data) {
    if (dirtyCache.check(key)) {
        const dirtyObject = dirtyCache.get(key);
        if (!dirtyObject.deleted) {
            if ((dirtyObject.title !== undefined) || (dirtyObject.is_archived !== undefined) ||
                 (dirtyObject.is_starred !== undefined) || (dirtyObject.tagList !== undefined)) {
                data.changed = true;
            }
            data.title = dirtyObject.title !== undefined ? dirtyObject.title : data.title;
            data.is_archived = dirtyObject.is_archived !== undefined ? dirtyObject.is_archived : data.is_archived;
            data.is_starred = dirtyObject.is_starred !== undefined ? dirtyObject.is_starred : data.is_starred;
            data.tagList =
            (dirtyObject.tagList !== undefined ? dirtyObject.tagList.split(',') : [])
            .concat(data.tags.map(t => t.label))
            .filter((v, i, a) => a.indexOf(v) === i)
            .join(',');
        } else {
            data.deleted = true;
        }
    }
    return data;
}

function applyDirtyCacheReal (key, data) {
    if (dirtyCache.check(key)) {
        const dirtyObject = dirtyCache.get(key);
        if (dirtyObject.deleted !== undefined) {
            return api.DeleteArticle(data.id).then(a => { dirtyCache.clear(key); });
        } else {
            if (data.changed !== undefined) {
                return api.PatchArticle(data.id, { title: data.title, starred: data.is_starred, archive: data.is_archived, tags: data.tagList })
                .then(data => cache.set(key, data))
                .then(a => { dirtyCache.clear(key); });
            }
        }
    }
    return data;
}
function cutArticle (data) {
    return Object.assign({}, {
        id: data.id,
        is_starred: data.is_starred,
        is_archived: data.is_archived,
        title: data.title,
        url: data.url,
        tags: data.tags,
        domain_name: data.domain_name,
        preview_picture: data.preview_picture
    });
}

function savePageToWallabag (url, resetIcon) {
    if (isServicePage(url)) {
        return;
    }
    // if WIP and was some dirty changes, return dirtyCache
    let exists = existCache.check(url) ? existCache.get(url) : existStates.notexists;
    if (exists === existStates.wip) {
        if (dirtyCache.check(url)) {
            let dc = dirtyCache.get(url);
            postIfConnected({ response: 'article', article: cutArticle(dc) });
        }
        return;
    }

    // if article was saved, return cache
    if (cache.check(url)) {
        postIfConnected({ response: 'article', article: cutArticle(cache.get(url)) });
        return;
    }

    // real saving
    setIcon(icons.wip);
    existCache.set(url, existStates.wip);
    postIfConnected({ response: 'info', text: 'Saving the page to wallabag ...' });
    api.SavePage(url)
            .then(data => applyDirtyCacheLight(url, data))
            .then(data => {
                if (!data.deleted) {
                    setIcon(icons.good);
                    postIfConnected({ response: 'article', article: cutArticle(data) });
                    cache.set(url, data);
                    saveExistFlag(url, existStates.exists);
                    if (api.data.AllowExistCheck === null || resetIcon) {
                        setTimeout(function () { setIcon(icons.default); }, 5000);
                    }
                } else {
                    cache.clear(url);
                }
                return data;
            })
            .then(data => applyDirtyCacheReal(url, data))
            .catch(error => {
                setIcon(icons.bad);
                setTimeout(function () { setIcon(icons.default); }, 5000);
                saveExistFlag(url, existStates.notexists);
                throw error;
            });
};

const GotoWallabag = (part) => api.checkParams() && browser.tabs.create({ url: `${api.data.Url}/${part}/list` });

const checkExist = (url) => {
    if (isServicePage(url)) { return; }
    if (existCache.check(url)) {
        const existsFlag = existCache.get(url);
        if (existsFlag === existStates.exists) {
            setIcon(icons.good);
        }
        if (existsFlag === existStates.wip) {
            setIcon(icons.wip);
        }
    } else {
        requestExists(url);
    }
};

const requestExists = (url) =>
        api.EntryExists(url)
        .then(data => {
            let icon = icons.default;
            if (data.exists) {
                icon = icons.good;
            }
            setIcon(icon);
            saveExistFlag(url, data.exists ? existStates.exists : existStates.notexists);
        });

const saveExistFlag = (url, exists) => {
    existCache.set(url, exists);
};

const isServicePage = (url) => /^(chrome|about|browser):(.*)/.test(url);
<commit_msg>resave article if it is deleted via web interface<commit_after>//* globals WallabagApi */

// declarations

if (typeof (browser) === 'undefined' && typeof (chrome) === 'object') {
    browser = chrome;
}

let Port = null;
let portConnected = false;

var CacheType = function (enable) {
    this.enabled = enable;
    this._cache = [];
};

CacheType.prototype = {
    _cache: null,
    enabled: null,

    str: function (some) {
        return btoa(JSON.stringify(some));
    },

    set: function (key, data) {
        if (this.enabled) {
            this._cache[this.str(key)] = data;
        }
    },

    clear: function (key) {
        if (this.enabled) {
            delete this._cache[this.str(key)];
        }
    },

    check: function (key) {
        return this.enabled && (this._cache[this.str(key)] !== undefined);
    },

    get: function (key) {
        return this.enabled ? this._cache[this.str(key)] : undefined;
    }
};

const icons = {
    'default': browser.runtime.getManifest().browser_action.default_icon,
    'good': 'img/wallabagger-green.svg',
    'wip': 'img/wallabagger-yellow.svg',
    'bad': 'img/wallabagger-red.svg'

};

const wallabagContextMenus = [
    {
        id: 'wallabagger-add-link',
        title: 'Wallabag it!',
        contexts: ['link', 'page']
    },
    {
        type: 'separator',
        contexts: ['browser_action']
    },
    {
        id: 'unread',
        title: 'Unread articles',
        contexts: ['browser_action']
    },
    {
        id: 'starred',
        title: 'Starred articles',
        contexts: ['browser_action']
    },
    {
        id: 'archive',
        title: 'Archived articles',
        contexts: ['browser_action']
    },
    {
        id: 'all',
        title: 'All articles',
        contexts: ['browser_action']
    },
    {
        id: 'tag',
        title: 'Tag list',
        contexts: ['browser_action']
    }
];

const existStates = {
    exists: 'exists',
    notexists: 'notexists',
    wip: 'wip'
};

const cache = new CacheType(true); // TODO - here checking option
const dirtyCache = new CacheType(true);
const existCache = new CacheType(true);

const api = new WallabagApi();

// Code

api.init().then(data => {
    addExistCheckListeners(api.data.AllowExistCheck);
    api.GetTags().then(tags => { cache.set('allTags', tags); });
});

addListeners();
createContextMenus();

// Functions
function createContextMenus () {
    wallabagContextMenus.map(menu => { browser.contextMenus.create(menu); });
}

function onTabActivatedListener (activeInfo) {
    setIcon(icons.default);
    const { tabId } = activeInfo;
    browser.tabs.get(tabId, function (tab) {
        checkExist(tab.url);
    });
}

function onTabCreatedListener (tab) {
    setIcon(icons.default);
}

function onTabUpdatedListener (tabId, changeInfo, tab) {
    if (changeInfo.status === 'loading' && tab.active) {
        saveExistFlag(tab.url, existStates.notexists);
        requestExists(tab.url);
    }
}

function addExistCheckListeners (enable) {
    if (enable) {
        browser.tabs.onActivated.addListener(onTabActivatedListener);
        browser.tabs.onCreated.addListener(onTabCreatedListener);
        browser.tabs.onUpdated.addListener(onTabUpdatedListener);
    } else {
        if (browser.tab && browser.tab.onActivated.hasListener(onTabActivatedListener)) {
            browser.tab.onActivated.removeListener(onTabActivatedListener);
        }
        if (browser.tab && browser.tabs.onCreated.hasListener(onTabCreatedListener)) {
            browser.tabs.onCreated.removeListener(onTabCreatedListener);
        }
        if (browser.tab && browser.tabs.onUpdated.hasListener(onTabUpdatedListener)) {
            browser.tabs.onUpdated.remoneListener(onTabUpdatedListener);
        }
    }
}

function onContextMenusClicked (info) {
    switch (info.menuItemId) {
        case 'wallabagger-add-link':
            if (info.linkUrl.length > 0) {
                savePageToWallabag(info.linkUrl, true);
            } else {
                savePageToWallabag(info.pageUrl, false);
            }
            break;
        case 'unread':
        case 'starred':
        case 'archive':
        case 'all':
        case 'tag':
            GotoWallabag(info.menuItemId);
            break;
    }
}

function onCommandsCommand (command) {
    if (command === 'wallabag-it') {
        browser.tabs.query({ 'active': true, 'currentWindow': true }, function (tabs) {
            if (tabs[0] != null) {
                savePageToWallabag(tabs[0].url, false);
            }
        });
    }
}

function postIfConnected (obj) {
    portConnected && Port.postMessage(obj);
    api.data.Debug && console.log(`postMessage: ${JSON.stringify(obj)}`);
}
function onPortMessage (msg) {
    try {
        switch (msg.request) {
            case 'save':
                savePageToWallabag(msg.tabUrl);
                break;
            case 'tags':
                if (!cache.check('allTags')) {
                    api.GetTags()
                            .then(data => {
                                postIfConnected({ response: 'tags', tags: data });
                                cache.set('allTags', data);
                            });
                } else {
                    postIfConnected({ response: 'tags', tags: cache.get('allTags') });
                }
                break;
            case 'saveTitle':
                if (msg.articleId !== -1) {
                    api.SaveTitle(msg.articleId, msg.title).then(data => {
                        postIfConnected({ response: 'title', title: data.title });
                        cache.set(msg.tabUrl, data);
                    });
                } else {
                    dirtyCacheSet(msg.tabUrl, {title: msg.title});
                }
                break;
            case 'deleteArticle':
                if (msg.articleId !== -1) {
                    api.DeleteArticle(msg.articleId).then(data => {
                        cache.clear(msg.tabUrl);
                    });
                } else {
                    dirtyCacheSet(msg.tabUrl, {deleted: true});
                }
                setIcon(icons.default);
                saveExistFlag(msg.tabUrl, existStates.notexists);
                break;
            case 'setup':
                postIfConnected({ response: 'setup', data: api.data });
                break;
            case 'setup-save':
                api.setsave(msg.data);
                postIfConnected({ response: 'setup-save', data: api.data });
                addExistCheckListeners(msg.data.AllowExistCheck);
                break;
            case 'setup-gettoken':
                api.setsave(msg.data);
                api.GetAppToken()
                        .then(a => {
                            api.save();
                            postIfConnected({ response: 'setup-gettoken', data: api.data, result: true });
                            if (!cache.check('allTags')) {
                                api.GetTags()
                                .then(data => { cache.set('allTags', data); });
                            }
                        })
                        .catch(a => {
                            postIfConnected({ response: 'setup-gettoken', data: api.data, result: false });
                        });
                break;
            case 'setup-checkurl':
                api.setsave(msg.data);
                api.CheckUrl()
                        .then(a => {
                            postIfConnected({ response: 'setup-checkurl', data: api.data, result: true });
                        })
                        .catch(a => {
                            api.clear();
                            // api.save();
                            postIfConnected({ response: 'setup-checkurl', data: api.data, result: false });
                        });
                break;
            case 'deleteArticleTag':
                if (msg.articleId !== -1) {
                    api.DeleteArticleTag(msg.articleId, msg.tagId).then(data => {
                        postIfConnected({ response: 'articleTags', tags: data.tags });
                        cache.set(msg.tabUrl, data);
                    });
                } else {
                    dirtyCacheSet(msg.tabUrl, {tagList: msg.tags});
                }
                break;
            case 'saveTags':
                if (msg.articleId !== -1) {
                    api.SaveTags(msg.articleId, msg.tags).then(data => {
                        postIfConnected({ response: 'articleTags', tags: data.tags });
                        cache.set(msg.tabUrl, data);
                    });
                } else {
                    dirtyCacheSet(msg.tabUrl, {tagList: msg.tags});
                }
                break;
            case 'SaveStarred':
            case 'SaveArchived':
                if (msg.articleId !== -1) {
                    api[msg.request](msg.articleId, msg.value ? 1 : 0).then(data => {
                        postIfConnected({ response: 'action', value: {starred: data.is_starred === 1, archived: data.is_archived === 1} });
                        cache.set(msg.tabUrl, data);
                    });
                } else {
                    dirtyCacheSet(msg.tabUrl, (msg.request === 'SaveStarred') ? {is_starred: msg.value ? 1 : 0} : {is_archived: msg.value ? 1 : 0});
                }
                break;
            default: {
                console.log(`unknown request ${JSON.stringify(msg)}`);
            }
        }
    } catch (error) {
        setIcon(icons.bad);
        setTimeout(function () { setIcon(icons.default); }, 5000);
        postIfConnected({ response: 'error', error: error });
    }
}

function onRuntimeConnect (port) {
    Port = port;
    portConnected = true;

    Port.onDisconnect.addListener(function () { portConnected = false; });
    Port.onMessage.addListener(onPortMessage);
}

function addListeners () {
    browser.contextMenus.onClicked.addListener(onContextMenusClicked);
    browser.commands.onCommand.addListener(onCommandsCommand);
    browser.runtime.onConnect.addListener(onRuntimeConnect);
}

function setIcon (icon) {
    browser.browserAction.setIcon({ path: icon });
}

function dirtyCacheSet (key, obj) {
    dirtyCache.set(key, Object.assign(dirtyCache.check(key) ? dirtyCache.get(key) : {}, obj));
    dirtyCache.set(key, Object.assign(dirtyCache.check(key) ? dirtyCache.get(key) : {}, { id: -1, url: key }));
}

function applyDirtyCacheLight (key, data) {
    if (dirtyCache.check(key)) {
        const dirtyObject = dirtyCache.get(key);
        if (!dirtyObject.deleted) {
            if ((dirtyObject.title !== undefined) || (dirtyObject.is_archived !== undefined) ||
                 (dirtyObject.is_starred !== undefined) || (dirtyObject.tagList !== undefined)) {
                data.changed = true;
            }
            data.title = dirtyObject.title !== undefined ? dirtyObject.title : data.title;
            data.is_archived = dirtyObject.is_archived !== undefined ? dirtyObject.is_archived : data.is_archived;
            data.is_starred = dirtyObject.is_starred !== undefined ? dirtyObject.is_starred : data.is_starred;
            data.tagList =
            (dirtyObject.tagList !== undefined ? dirtyObject.tagList.split(',') : [])
            .concat(data.tags.map(t => t.label))
            .filter((v, i, a) => a.indexOf(v) === i)
            .join(',');
        } else {
            data.deleted = true;
        }
    }
    return data;
}

function applyDirtyCacheReal (key, data) {
    if (dirtyCache.check(key)) {
        const dirtyObject = dirtyCache.get(key);
        if (dirtyObject.deleted !== undefined) {
            return api.DeleteArticle(data.id).then(a => { dirtyCache.clear(key); });
        } else {
            if (data.changed !== undefined) {
                return api.PatchArticle(data.id, { title: data.title, starred: data.is_starred, archive: data.is_archived, tags: data.tagList })
                .then(data => cache.set(key, data))
                .then(a => { dirtyCache.clear(key); });
            }
        }
    }
    return data;
}
function cutArticle (data) {
    return Object.assign({}, {
        id: data.id,
        is_starred: data.is_starred,
        is_archived: data.is_archived,
        title: data.title,
        url: data.url,
        tags: data.tags,
        domain_name: data.domain_name,
        preview_picture: data.preview_picture
    });
}

function moveToDirtyCache (url) {
    if (cache.check(url)) {
        let art = cache.get(url);
        // api.data.Debug && console.log(`article to move to dirtyCache ${JSON.stringify(art)}`);
        dirtyCacheSet(url, {
            title: art.title,
            tagList: art.tags.map(tag => tag.label).join(','),
            is_archived: art.is_archived,
            is_starred: art.is_starred
        });
        cache.clear(url);
    }
}

function savePageToWallabag (url, resetIcon) {
    if (isServicePage(url)) {
        return;
    }
    // if WIP and was some dirty changes, return dirtyCache
    let exists = existCache.check(url) ? existCache.get(url) : existStates.notexists;
    if (exists === existStates.wip) {
        if (dirtyCache.check(url)) {
            let dc = dirtyCache.get(url);
            postIfConnected({ response: 'article', article: cutArticle(dc) });
        }
        return;
    }

    // if article was saved, return cache
    if (cache.check(url)) {
        postIfConnected({ response: 'article', article: cutArticle(cache.get(url)) });
        // check if article was deleted via web interface
        requestExists(url)
        .then(exists => {
            if (!exists) {
                moveToDirtyCache(url);
                savePageToWallabag(url, resetIcon);
            }
        });
        return;
    }

    // real saving
    setIcon(icons.wip);
    existCache.set(url, existStates.wip);
    postIfConnected({ response: 'info', text: 'Saving the page to wallabag ...' });
    api.SavePage(url)
            .then(data => applyDirtyCacheLight(url, data))
            .then(data => {
                if (!data.deleted) {
                    setIcon(icons.good);
                    postIfConnected({ response: 'article', article: cutArticle(data) });
                    cache.set(url, data);
                    saveExistFlag(url, existStates.exists);
                    if (api.data.AllowExistCheck === null || resetIcon) {
                        setTimeout(function () { setIcon(icons.default); }, 5000);
                    }
                } else {
                    cache.clear(url);
                }
                return data;
            })
            .then(data => applyDirtyCacheReal(url, data))
            .catch(error => {
                setIcon(icons.bad);
                setTimeout(function () { setIcon(icons.default); }, 5000);
                saveExistFlag(url, existStates.notexists);
                throw error;
            });
};

const GotoWallabag = (part) => api.checkParams() && browser.tabs.create({ url: `${api.data.Url}/${part}/list` });

const checkExist = (url) => {
    if (isServicePage(url)) { return; }
    if (existCache.check(url)) {
        const existsFlag = existCache.get(url);
        if (existsFlag === existStates.exists) {
            setIcon(icons.good);
        }
        if (existsFlag === existStates.wip) {
            setIcon(icons.wip);
        }
    } else {
        requestExists(url);
    }
};

const requestExists = (url) =>
        api.EntryExists(url)
        .then(data => {
            let icon = icons.default;
            if (data.exists) {
                icon = icons.good;
            }
            setIcon(icon);
            saveExistFlag(url, data.exists ? existStates.exists : existStates.notexists);
            return data.exists;
        });

const saveExistFlag = (url, exists) => {
    existCache.set(url, exists);
};

const isServicePage = (url) => /^(chrome|about|browser):(.*)/.test(url);
<|endoftext|>


<commit_before>import fs from "fs";
import request from "request-promise";
import HtmlParser from "./HtmlParser.js";

class DeliciousHtmlExporter {

  constructor({username, baseEndpoint = 'https://del.icio.us', writeHtmlToDisk = false}) {
    this.username = username;
    this.baseEndpoint = baseEndpoint;
    this.writeHtmlToDiskEnabled = writeHtmlToDisk;
    this.parser = new HtmlParser();
  }

  fetch() {
    return new Promise((resolve, reject) => {
      this.fetchPage()
          .then(response => {
            let combinedResult = {
              title: response.title,
              pages: response.page.total,
              totalElements: response.page.totalElements,
              items: []
            };
            console.log(`Fetched initial page, and now we know how many to load: ${response.page.total} pages, ${response.page.totalElements} items`);
            return this.collectAndFetchNext(response, combinedResult);
          })
          .then(resolve, reject);
    });
  }

  fetchPage(page = 1) {
    return request(`${this.baseEndpoint}/${this.username}?page=${page}`)
      .then((html) => {
        if (this.writeHtmlToDiskEnabled) {
          this.writeHtmlToDisk(page, html);
        }
        return this.parser.parse(html);
      })
      .then(response => {
        console.log(`Fetched page ${response.page.number}/${response.page.total}`);
        return response;
      });
  }

  collectAndFetchNext(currentResponse, combinedResult) {
    return new Promise((resolve, reject) => {
      combinedResult.items.concat(currentResponse.items);
      if (currentResponse.page.next) {
        this.fetchPage(currentResponse.page.next)
            .then(nextResponse => this.collectAndFetchNext(nextResponse, combinedResult))
            .then(resolve, reject);
      } else {
        resolve(combinedResult);
      }
    });
  }

  writeHtmlToDisk(page, htmlString) {
    fs.writeFile(`output/page_${page}.html`, htmlString);
  }

}

export default DeliciousHtmlExporter;
<commit_msg>Add local file loader support<commit_after>import fs from "fs";
import request from "request-promise";
import HtmlParser from "./HtmlParser.js";

class RemoteHttpLoader {

  constructor(baseEndpoint, username) {
    this.username = username;
    this.baseEndpoint = baseEndpoint;
  }

  loadPage(page) {
    return new Promise((resolve, reject) => {
      request(`${this.baseEndpoint}/${this.username}?page=${page}`)
        .then(resolve, reject);
    });
  }

}

class LocalFileLoader {

  constructor(filePrefix, fileSuffix) {
    this.filePrefix = filePrefix;
    this.fileSuffix = fileSuffix;
  }

  loadPage(page) {
    return new Promise((resolve, reject) => {
      fs.readFile(`${this.filePrefix}${page}${this.fileSuffix}`, 'utf8', (err, data) => {
        if (err) {
          reject({
                   message: err.message
                 });
        } else {
          resolve(data.toString());
        }
      });
    });
  }

}

class DeliciousHtmlExporter {

  constructor({username, baseEndpoint = 'https://del.icio.us', writeHtmlToDisk = false}) {
    this.filePrefix = 'output/page_';
    this.fileSuffix = '.html';
    this.username = username;
    this.baseEndpoint = baseEndpoint;
    this.writeHtmlToDiskEnabled = writeHtmlToDisk;
    this.loader = new LocalFileLoader(this.filePrefix, this.fileSuffix);
    //this.loader = new RemoteHttpLoader(this.baseEndpoint, this.username);
    this.parser = new HtmlParser();
  }

  fetch() {
    return new Promise((resolve, reject) => {
      this.fetchPage()
          .then(response => {
            let combinedResult = {
              title: response.title,
              pages: response.page.total,
              totalElements: response.page.totalElements,
              items: []
            };
            console.log(`Fetched initial page, and now we know how many to load: ${response.page.total} pages, ${response.page.totalElements} items`);
            return this.collectAndFetchNext(response, combinedResult);
          })
          .then(resolve, reject);
    });
  }

  fetchPage(page = 1) {
    return this.loader.loadPage(page)
               .then((html) => {
                 if (this.writeHtmlToDiskEnabled) {
                   this.writeHtmlToDisk(page, html);
                 }
                 return this.parser.parse(html);
               })
               .then(response => {
                 console.log(`Fetched page ${response.page.number}/${response.page.total}`);
                 return response;
               });
  }

  collectAndFetchNext(currentResponse, combinedResult) {
    return new Promise((resolve, reject) => {
      combinedResult.items.concat(currentResponse.items);
      if (currentResponse.page.next) {
        this.fetchPage(currentResponse.page.next)
            .then(nextResponse => this.collectAndFetchNext(nextResponse, combinedResult))
            .then(resolve, reject);
      } else {
        resolve(combinedResult);
      }
    });
  }

  writeHtmlToDisk(page, htmlString) {
    fs.writeFile(`${this.filePrefix}${page}${this.fileSuffix}`, 'utf8', htmlString);
  }

}

export default DeliciousHtmlExporter;
<|endoftext|>