/
inject.js
197 lines (177 loc) · 6.97 KB
/
inject.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
// dependency: constants.js, storage.js, api.js
var volatileIsGetRidLimit = false; // aim to use this variable as short period as much as possible, re-read from storage for two keys (kUserPurchasedLifetimeAPI and kUserVerifiedLicense; former has higher priority)
var exceptionArray = [];
// find owner username first
var ownerHandleDom = document.querySelector('div.DashboardProfileCard-content span.username > b');
var ownerHandle = null;
if (ownerHandleDom != null) {
ownerHandle = ownerHandleDom.innerHTML || ownerHandle.textContent;
ownerHandle = ownerHandle.toLowerCase();
}
// get rid of tweets according to current limitation set
function getRid() {
// query dom element (twitter card) that is not you, yes, only show your tweets
var tweets = document.querySelectorAll('div.tweet');
for (var i=0; i <tweets.length; i++) {
var t = tweets[i];
// find handle of such tweet
var handleDom = t.querySelector('span.username > b');
if (handleDom != null) {
var handle = handleDom.innerHTML || handleDom.textContent;
handle = handle.toLowerCase();
if (handle != ownerHandle && !matchHandle(handle)) {
t.remove();
}
}
}
}
// check whether input handle matches any of exceptions
function matchHandle(handle) {
var limit = volatileIsGetRidLimit ? constants.trialSettings.kExceptionsLimit : exceptionArray.length;
for (var i=0; i<limit; i++) {
if (handle == exceptionArray[i]) {
return true;
}
}
return false;
}
// parse raw exceptions as array
function parseExceptionsAsArray(rawExceptions) {
var tokens = rawExceptions.split(',');
var retTokens = [];
for (var i=0; i<tokens.length; i++) {
var t = tokens[i].replace(' ', '');
retTokens.push(t.toLowerCase());
}
return retTokens;
}
function updateGetRidLimitStatus() {
// we need to read from two keys from storage
// kUserPurchasedLifetimeAPI and kUserVerifiedLicense
// the first one has higher priority
loadUserPurchasedLifetimeIAP(function(purchased) {
if (purchased && volatileIsGetRidLimit) {
// no limit
volatileIsGetRidLimit = false;
// no need to check another one as this has higher priority
log('volatileIsGetRidLimit 1: ' + volatileIsGetRidLimit);
}
else if (!purchased) {
// load kUserVerifiedLicense
loadUserVerifiedLicense(function(verified) {
volatileIsGetRidLimit = verified ? false : true;
log('volatileIsGetRidLimit 2: ' + volatileIsGetRidLimit);
});
}
});
}
// begin operation only if detect owner handle
if (ownerHandle != null) {
// firstly try to load exception list
loadExceptionsFromStorage(function(rawExceptions) {
if (rawExceptions != null) {
// parse exception into array
exceptionArray = parseExceptionsAsArray(rawExceptions);
}
});
// update getRid() limit status flag
updateGetRidLimitStatus();
// solution from https://stackoverflow.com/questions/3219758/detect-changes-in-the-dom
// modified to fit our problem domain
var observeDOM = (function(op){
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver,
eventListenerSupported = window.addEventListener;
return function(obj, callback){
if( MutationObserver ){
// define a new observer
var obs = new MutationObserver(function(mutations, observer){
if( mutations[0].addedNodes.length || mutations[0].removedNodes.length )
callback();
});
// only listen to childlist, no need for subtree
// this will trigger event just one time
obs.observe( obj, { childList:true, subtree:false });
}
else if( eventListenerSupported ){
obj.addEventListener('DOMNodeInserted', callback, false);
}
};
})();
// observe for "See x new Tweets" bar shown up
var newItemBarElem = document.querySelector('div.stream-container > div.stream-item.js-new-items-bar-container');
if (newItemBarElem != null) {
observeDOM(newItemBarElem, function() {
// find a bar
var newtweetBarButton = newItemBarElem.querySelector('button.new-tweets-bar');
if (newtweetBarButton != null) {
// click on it to expand
newtweetBarButton.click();
// now new elements added into DOM, getRid() will handle it
// because we've listened to such event already
}
});
}
// observe for tweets as shown in stream
observeDOM(document.getElementById('stream-items-id'), function() {
getRid();
});
// listen to message sent by popup script
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
log(request);
// exception data
if (request.key != null && request.key == constants.messageKey.kExceptions) {
//sendResponse({ack: 'i got it!'});
// parse raw exceptions as array, and set to array
exceptionArray = parseExceptionsAsArray(request.message);
// apply new exceptions right away
getRid();
}
// user intends to buy iap
else if (request.key != null && request.key == constants.messageKey.kIntendToBuyIAP) {
//sendResponse({ack: 'i got that you wanna buy iap!'});
// buy lifetie iap
buyLifetimeIAP(function() {
log('user cancelled iap widow, or failed to buy');
// fix (workaround): issue of successfully buying not return in success path
verifyPurchasedLifetimeIAP(function(purchased) {
// save status to storage
saveValueToStorage(constants.storageKeys.kUserPurchasedLifetimeIAP, purchased, function() {
log('cant save purchasing status to storage');
}, function() {
log('saved purchasing status to storage.');
});
// update limit only if purchase successfully
if (purchased) {
volatileIsGetRidLimit = false;
// immediately getRid()
getRid();
}
});
}, function(response) {
log('bought iap:', response);
// save status to storage
saveValueToStorage(constants.storageKeys.kUserPurchasedLifetimeIAP, true, function() {
log('cant save purchasing status to storage');
}, function() {
log('saved purchasing status to storage.');
});
// for safety as if saving to storage might have error
// always relax getRid() limit and immediately call getRid()
// directly set limit flag
volatileIsGetRidLimit = false;
// immediately apply getRid()
getRid();
});
}
// execute getRid()
else if (request.key != null && request.key == constants.messageKey.kExecuteGetRid) {
//sendResponse({ack: 'i got that you want to execute getRid()'});
getRid();
}
// notified with updated of purchase iAP status
else if (request.key != null && request.key == constants.messageKey.kNotifyUpdatedGetRidLimit) {
//sendResponse({ack: 'i got that you have updated something that affects getRid() limit'});
updateGetRidLimitStatus();
}
});
}