I'm writing a tampermonkey script that I want to perform an action when a certain set of keys is pressed (cmd + alt + t). I'm seeing a problem where the code stops executing altogether after I click anywhere on the page and I have to either hard refresh or click a certain place on the page (search bar in CloudWatch) to get it working again.
Here's my code:
(async function() {
var set = new Set();
function funcA() {
console.log('funcA');
}
function funcB() {
console.log('funcB');
if (set.has(91) && set.has(18) && set.has(84)) { // cmd + alt + t
funcA();
}
}
function onKeyUp(e) {
console.log('onKeyUp');
set.clear();
console.log(new Array(...set).join(' '));
}
function onKeyDown(e) {
console.log('onKeyDown');
set.add(e.which);
console.log(new Array(...set).join(' '));
funcB();
}
function init() {
console.log('init');
console.log('Adding event listeners.');
document.addEventListener('keyup', onKeyUp);
document.addEventListener('keydown', onKeyDown);
}
if( document.readyState !== 'loading' ) {
console.log( 'not loading' );
init();
} else {
document.addEventListener('DOMContentLoaded', init);
}
})();
The problem only seems to happen on certain pages, for example in AWS CloudWatch logs.
Any ideas?
I'm using Visual Studio>TypeScript>Cordova application to build foundation of my application. then i install angularJs 1.3 at it was required by mobile-angular-ui, and then install it.
The application has index.ts, which later it's compailation result will be placed inside appBundle.js ....
The file is like this and work just fine:
// For an introduction to the Blank template, see the following documentation:
// http://go.microsoft.com/fwlink/?LinkID=397705
// To debug code on page load in Ripple or on Android devices/emulators: launch your app, set breakpoints,
// and then run "window.location.reload()" in the JavaScript Console.
var RavisMobileCordova;
(function (RavisMobileCordova) {
"use strict";
var Application;
(function (Application) {
function initialize() {
document.addEventListener('deviceready', onDeviceReady, false);
}
Application.initialize = initialize;
function onDeviceReady() {
// Handle the Cordova pause and resume events
document.addEventListener('pause', onPause, false);
document.addEventListener('resume', onResume, false);
// TODO: Cordova has been loaded. Perform any initialization that requires Cordova here.
}
function onPause() {
// TODO: This application has been suspended. Save application state here.
}
function onResume() {
// TODO: This application has been reactivated. Restore application state here.
}
})(Application = RavisMobileCordova.Application || (RavisMobileCordova.Application = {}));
window.onload = function () {
Application.initialize();
};
})(RavisMobileCordova || (RavisMobileCordova = {}));
//# sourceMappingURL=appBundle.js.map
but when it's come to the stage, that i may be in need of calling an events inside my controller, i can't do that, the debugger detach and doesn't show any help full error.
i tried it with use of onResume for test propose, first by seeing this:
https://groups.google.com/forum/#!topic/angular/nx3Csp8nu0A
and replacing the line :
document.addEventListener('resume', onResume, false);
with the following, and defining $scope.onResume=function... in my controller:
document.addEventListener('resume', $scope.onResume, false);
Failed!!!
Then i done document.addEventListener(...) within my own controller
Failed!!!
then i look closer at appBundle.js and saw if register on resume just when device ready is called, so i did this within the body of controller:
//Only Device Ready Here
document.addEventListener('deviceready', $scope.DeviceEvents.onDeviceReady, false);
$scope.DeviceEvents={
onDeviceReady: function(){
//Register Device Events Here
document.addEventListener('resume', $scope.DeviceEvents.onResume, false);
},
onResume:function(){
alert("resuming!!!");
}
};
And again,
Failed!!!
Now, does any one knows how should i do it side by mobile-angular-ui? how to register any kinds of event inside the app, which come from device.
If i'm wrong any where, Sorry, I'm new to all of these stuff, Android, Cordova, and know the basic of Angular, ... someone who's boss told him to write an application, that i'm not familiar with any part of it.
Thank you,
Hassan F.
Edit
I also find this just now, it been used withing the angular it self, but it still doesn't have any thing to do with the controller...
MyModule.run(function ($rootScope, $http, dataService, $window, $q, $location, localize) {
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
//Initialize anything you need to. aka: Google analytics.
//Set other evens you need to listen to.
document.addEventListener("online", onOnline, false);
document.addEventListener("offline", onOffline, false);
}
}
here's what i did... First, i remove the ng-app, from my HTML
then i register onDeviceReady event, and inside that, then i append ng-app to my html, so after device become ready, angular will start it's processes.
Second, i have multi-layered structure, which mean, i have a master page, which sit on HTML tag, and other controller are within this one, using ng-view and route config.
so i bind events to my master controller:
like this:
module RavisMobileCordova {
export module Application {
export function initialize() {
var matchesDevice = navigator.userAgent.match(/(iPhone|iPod|iPad|Android|BlackBerry|IEMobile)/);
isBrowser = !(matchesDevice!=null && matchesDevice.length > 0);
document.addEventListener("deviceready", onDeviceReady, false);
if (isBrowser) {
// Create the event
var event = new CustomEvent("deviceready", {});
// Dispatch/Trigger/Fire the event
document.dispatchEvent(event);
}
}
function onDeviceReady(evt) {
angular.element(document).ready(() => {
var domElement = document.getElementById("appElement");
angular.bootstrap(domElement, ["ravisMobileApp"]);
});
// Handle the Cordova pause and resume events
//TRUE: Capture Phase, FALSE: Bubbling Phase
document.addEventListener("pause", onPause, false);
document.addEventListener("resume", onResume, false);
document.addEventListener("backbutton", onBackButton, true);
document.addEventListener("offline", onOffline, false);
document.addEventListener("online", onOnline, false);
// TODO: Cordova has been loaded. Perform any initialization that requires Cordova here.
var scope:any = angular.element("[ng-controller='MasterController'], [data-ng-controller='MasterController']").scope();
scope.SystemApi.Navigator = navigator;
scope.SystemEvents.OnStart(evt);
}
function onPause() {
// TODO: This application has been suspended. Save application state here.
}
function onResume() {
// TODO: This application has been reactivated. Restore application state here.
}
function onBackButton(evt) {
//evt.preventDefault();
//evt.stopPropagation();
var scope:any = angular.element("[ng-controller='MasterController'], [data-ng-controller='MasterController']").scope();
scope.SystemEvents.BackButton(evt);
}
function onOffline() {
var scope: any = angular.element("[ng-controller='MasterController'], [data-ng-controller='MasterController']").scope();
scope.SystemEvents.OnOffline();
}
function onOnline() {
var scope: any = angular.element("[ng-controller='MasterController'], [data-ng-controller='MasterController']").scope();
scope.SystemEvents.OnOnline();
}
}
window.onload = () => {
Application.initialize();
}
}
then i define this section within my master controller, which get called on registerd events...
$scope.SystemEvents = {
OnStart: function () {
LocalStorageService.NavigatorHistory.clearHistory();
angular.element("#master_overlay").removeAttr("style");
if ($scope.SystemMethods.IsNetworkOffline()) {
//$scope.SystemEvents.OnOffline();
// Create the event
var event = new CustomEvent("offline", {});
// Dispatch/Trigger/Fire the event
document.dispatchEvent(event);
}
},
OnOnline: function () {
if (!Utility.isNullOrUndefined(eventList[EventTypes.Online])) {
for (var i = 0; i < eventList[EventTypes.Online].length; i++) {
eventList[EventTypes.Online][i].callBack();
}
}
$scope.SystemMethods.HideMasterOverlay();
},
OnOffline: function() {
if (!Utility.isNullOrUndefined(eventList[EventTypes.Offline])) {
for (var i = 0; i < eventList[EventTypes.Offline].length; i++) {
eventList[EventTypes.Offline][i].callBack();
}
}
$scope.SystemMethods.ShowMasterOverlay();
},
BackButton: function (evt) {
//Bind Cancel
evt.cancel = false;
//Call All Registered Events
if (!Utility.isNullOrUndefined(eventList[EventTypes.BackButton])) {
for (var i = 0; i < eventList[EventTypes.BackButton].length; i++) {
eventList[EventTypes.BackButton][i].callBack(evt);
}
}
//If Cancel Do Return
if (evt.cancel)
return;
//TODO: API Cancelable Code Here
$scope.SystemMethods.MoveToPreviousPage();
//evt.preventDefault();
//evt.stopPropagation();
},
HistoryBack: $scope.SystemMethods.MoveToPreviousPage
};
now if i ever need these within my master page, i do what i need in here, else, if i need them in my other controllers...
here's what i gonna do:
Master Controller:
var eventList = {};
$scope.SystemMethods = {
RegisterEvent: function(eventType, callBack) {
if (Utility.isNullOrUndefined(eventList[eventType]))
eventList[eventType] = [];
eventList[eventType].push({ eventType: eventType, callBack: callBack });
},
UnregisterEvent: function(eventType, callBack) {
var delList = Enumerable.From(eventList[eventType]).Where(function (w) {
return w.eventType === eventType && w.callBack === callBack;
}).ToArray();
for (var i = 0; i < delList.length; i++) {
var objIndex = Enumerable.From(eventList[eventType]).IndexOf(delList[i]);
eventList[eventType].splice(objIndex, 1);
}
}
}
Other controllers:
You also have to unregister what you register, since your master controller is persist and you pass your function to the variable within that controller, they won't go away by their own selves
$scope.initialize = function () {
$scope.SystemMethods.RegisterEvent(EventTypes.BackButton, $scope.Events.backButton);
$scope.SystemMethods.RegisterEvent(EventTypes.Online, $scope.Events.online);
}
$scope.$on("$destroy", function () {
$scope.SystemMethods.UnregisterEvent(EventTypes.Online, $scope.Events.online);
$scope.SystemMethods.UnregisterEvent(EventTypes.BackButton, $scope.Events.backButton);
});
I am building an with cordova.js library [version 3.4.1] and I would also like to debug it as web page directly using the web browser.
There are some issues in iOS emulator when loading dynamically the external cordova.js library [I have 2 version specific for android and iOS]
I have this piece of code in place to deal with this:
//check if mobile or local browser:
var isMobile = true;
if (document.URL.indexOf("local") > 0) {
isMobile = false;
}
var deviceReadyDeferred = $.Deferred();
var jqmReadyDeferred = $.Deferred();
function onDeviceReady () {
deviceReadyDeferred.resolve();
}
if (isMobile) {
$(document).bind('mobileinit', function () {
$.mobile.allowCrossDomainPages = true;
jqmReadyDeferred.resolve();
var useragent = navigator.userAgent;
var loadScript = function (url) {
url = url.replace(/\//, '\\/');
document.write('<script charset="utf-8" src="' + url + ' "><\/script>');
};
if (/Android/i.test(useragent)) {
$.getScript('js/lib/cordova_android.js', function (data, textStatus, jqxhr) {
document.addEventListener("deviceReady", onDeviceReady, false);
});
} else {
loadScript('js/lib/cordova_ios.js');
setTimeout(function () {
document.addEventListener("deviceReady", onDeviceReady, false);
}, 500);
}
});
} else {
jqmReadyDeferred.resolve();
onDeviceReady();
}
So when the page is requested with localhost... then the isMobile is set to false.
Is there a reason why iOS [6.1] does not load the external script like Android did [with jQuery getscript ] instead than the horrible hack ? I tried to debug and it seems that iOS is reporting status error 400 when requesting the script.
Followed the logic used in this other SO question:
Issue with dynamically loaded phonegap.js
UPDATE:
Following the suggestion of the accepted answer, I rewrote the whole function and now it is working well both in iOS / ANDROID and local browser:
var deviceReadyDeferred = $.Deferred();
var jqmReadyDeferred = $.Deferred();
(function () {
//check if mobile or local browser:
var isMobile = true;
var useragent = navigator.userAgent;
var cordova_js = 'cordova_';
if (/Android/i.test(useragent)) {
cordova_js += 'android.js'
} else if ((/iPhone/i.test(useragent)) || (/iPad/i.test(useragent))) {
cordova_js += 'ios.js'
} else {
// local browser testing
isMobile = false;
jqmReadyDeferred.resolve();
onDeviceReady();
}
if (isMobile) {
$(document).bind('mobileinit', function () {
$.mobile.allowCrossDomainPages = true;
jqmReadyDeferred.resolve();
var url = document.URL;
url = url.substring(0, url.lastIndexOf("/"));
$.getScript(url + '/js/lib/' + cordova_js, function (data, textStatus, jqxhr) {
document.addEventListener("deviceReady", onDeviceReady, false);
});
});
}
function onDeviceReady () {
deviceReadyDeferred.resolve();
}
})();
Your isMobile check is prone to break. Think of a remote URL like this:
http://www.somesite.com/local/foo/bar
And probably the "local" URL in iOS looks different. Try logging the document.URL in iOS to check, or maybe show it in an alert if console is not an option.
I have the following code which when embedded within a loaded html page works successfully:
<HEAD>
<script language="javascript" type="text/javascript">
document.addEventListener('DOMContentLoaded', function ()
{
document.documentElement.addEventListener("touchstart", function (e)
{
if (['A', 'BUTTON'].indexOf(e.target.tagName) === -1)
{
e.preventDefault();
e.stopPropagation();
invokeObjectiveC("ontouchstart:");
}
else
{
e.preventDefault();
e.stopPropagation();
invokeObjectiveC(e.target);
}
}, true);
}, false);
function invokeObjectiveC(action) {
...
}
However if I take it out of the html and try to load it separatly then it is not working.
When loading it separately it looks like this:
alert('test');
function addListener()
{
alert('adding listener');
document.addEventListener('DOMContentLoaded', function ()
{
document.documentElement.addEventListener("touchstart", function (e)
{
alert('touchstart');
if (['A', 'BUTTON'].indexOf(e.target.tagName) === -1)
{
e.preventDefault();
e.stopPropagation();
invokeObjectiveC("ontouchstart:");
}
else
{
e.preventDefault();
e.stopPropagation();
invokeObjectiveC(e.target);
}
}, true);
}, false);
}
addListener();
function invokeObjectiveC(action) {
...
}
When added, a "test" and an "adding listener" alert dialog are displayed so I know this part is working.
However the code then doesn't work - its supposed to detect touches (this is on iOS) in certain parts of the screen.
With the code within the html everything works fine and it executes when the screen is touched, when its loaded separatly nothing happens when the screen is touched.
Any ideas what the problem could be?
====== UPDATE
I tried running the following during various stages of the lifecycle, including before the request to even load the page has been issued, but 'Dom content loaded' never appears:
var script = document.createElement('script');
script.type = 'text/javascript';
script.text =
var Test =
{
f: function()
{
if (!event.originalTarget.defaultView.frameElement)
{
alert('DOM content loaded');
}
}
}
function addTestListener()
{
alert('adding listener');
window.addEventListener("DOMContentLoaded", function(e) { Test.f(); }, false);
}
document.getElementsByTagName('head')[0].appendChild(script);
From your comment about the iOS function, I suspect that the document is already loaded when you try to execute your javascript. If that's the case, then the DOMContentLoaded event has already happened so you code will never be initialized. You can fix that by checking to see if the document is already loaded by changing your code to this:
function addListener() {
alert('adding listener');
function init() {
document.documentElement.addEventListener("touchstart", function (e) {
alert('touchstart');
if (['A', 'BUTTON'].indexOf(e.target.tagName) === -1) {
e.preventDefault();
e.stopPropagation();
invokeObjectiveC("ims-ontouchstart:");
} else {
e.preventDefault();
e.stopPropagation();
invokeObjectiveC(e.target);
}
}, true);
}
if (document.readyState === "complete") {
init();
} else {
document.addEventListener('DOMContentLoaded', init, false);
}
}
addListener();
function invokeObjectiveC(action) {
...
}
I changed the line
function addListener()
into
window.onload = function addListener()
and it seems to have done the trick
I have the following code snippet to enable extension after firefox quit,
observe: function (subject, topic, data) {
if (topic == "quit-application") {
LOG("inside quit-application Testing ");
var os = Components.classes["#mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
//os.addObserver(this, "http-on-examine-cached-response", false);
os.addObserver(this, "quit-application", false);
var appInfo = Components.classes["#mozilla.org/xre/app-info;1"]
.getService(Components.interfaces.nsIXULAppInfo);
var tempappVersion = appInfo.version;
var appVersion = tempappVersion.split(".");
// adding add-on listener dsable event on add-on for FF4 and later versions.
if (appVersion[0] >= 4) {
setAddonEnableListener();
LOG("\napp-startup Testing from javascript file....");
}
return;
}
}
And inside the setAddonEnableListener I am trying to enable the extension like this:
function setAddonEnableListener() {
try {
alert("setAddonEnableListener akbar nsListener called from ");
Components.utils.import("resource://gre/modules/AddonManager.jsm");
AddonManager.getAddonByID("somename#extension.com", function(addon)
{
if (addon.userDisabled)
addon.userDisabled = false;
});
} catch (ex) {
}
}
And I am registering the quit-application event var like this:
myModule = {
registerSelf: function (compMgr, fileSpec, location, type) {
var compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
compMgr.registerFactoryLocation(this.myCID,
this.myName,
this.myProgID,
fileSpec,
location,
type);
var catMgr = Components.classes["#mozilla.org/categorymanager;1"].getService(Components.interfaces.nsICategoryManager);
catMgr.addCategoryEntry("app-startup", this.myName, this.myProgID, true, true);
catMgr.addCategoryEntry("quit-application", this.myName, this.myProgID, true, true);
}
When Firefox quits, inside quit-application Testing message is not getting displayed. What am I doing wrong here?
There is no category quit-application. You should receive app-startup notification (or rather profile-after-change starting with Firefox 4) and register your observer for quit-application:
observe: function (subject, topic, data) {
if (topic == "app-startup" || topic == "profile-after-change") {
var observerService = Components.classes["#mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
observerService.addObserver(this, "quit-application", false);
}
else if (topic == "quit-application") {
var observerService = Components.classes["#mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
observerService.removeObserver(this, "quit-application");
...
}
}
To quote the documentation:
Unless otherwise noted you register for the topics using the nsIObserverService.
And there is no note about registration via category manager on any shutdown notifications.
Btw, I sincerely recommend XPCOMUtils for component registration. You shouldn't need to write the module definition yourself.