forked from mozilla/zamboni
/
mobile_buttons.js
232 lines (204 loc) · 8.56 KB
/
mobile_buttons.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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
(function() {
/* Call this with something like $('.install').installButton(); */
z.button = {};
/* A library of callbacks that may be run after InstallTrigger succeeds.
* ``this`` will be bound to the .install button.
*/
z.button.after = {'contrib': function(xpi_url, status) {
if (status === 0) { //success
document.location = $(this).attr('data-developers');
}
}};
/* Install an XPI or a JAR (or something like that).
*
* hash and callback are optional. callback is triggered after the
* installation is complete.
*/
z.installAddon = function(name, url, icon, hash, callback) {
var params = {};
params[name] = {
URL: url,
IconURL: icon,
toString: function() { return url; }
};
if (hash) {
params[name]['Hash'] = hash;
}
// InstallTrigger is a Gecko API.
InstallTrigger.install(params, callback);
};
z.installSearch = function(name, url, icon, hash, callback) {
if (window.external && window.external.AddSearchProvider) {
window.external.AddSearchProvider(url);
callback();
} else {
// Alert! Deal with it.
alert(gettext('Sorry, you need a Mozilla-based browser (such as Firefox) to install a search plugin.'));
}
};
var messages = {
'tooNew': format(gettext("Not Updated for {0} {1}"), z.appName, z.browserVersion),
'tooOld': format(gettext("Requires Newer Version of {0}"), z.appName),
'unreviewed': gettext("Unreviewed"),
'badApp': format(gettext("Not Available for {0}"), z.appName),
'badPlatform': format(gettext("Not Available for {0}"), z.platformName),
'experimental': gettext("Experimental")
};
function Button(el) {
// the actionQueue holds all the various events that have to happen
// when the user clicks the button. This includes the terminal action,
// such as "install", "purchase", or "add to mobile".
// actions are a tuple of the form [n, cb], where cb is a method that
// is called when the action is executed, and n is the priority of the
// action. The queue is sorted before execution. the callback is
// executed in the function's scope. To resume, call this.nextAction()
// after a user or other blocking action, or return true to
// immediately execute the next action.
this.actionQueue = [];
var self = this,
attr, classes,
hashes = {},
errors = [],
warnings = [],
activeInstaller,
currentAction=0,
//setup references to DOM UI.
dom = {
'self' : $(el),
'badges' : $(el).find(".badges"),
//Can be multiple buttons in the case of platformers
'buttons' : $('.button', el),
'labels' : $('.button span', el)
};
// the initializer is called once when the button is created.
this.init = function() {
initFromDom();
collectHashes();
versionPlatformCheck();
this.actionQueue.push([0, function() {
var href = activeInstaller.attr('href'),
hash = hashes[href],
attr = self.attr,
install = attr.search ? z.installSearch : z.installAddon;
install(attr.name, href, attr.icon, hash);
return true;
}]);
for (var i=0; i<errors.length; i++) {
dom.badges.append(format("<li class='error'>{0}</li>", messages[errors[i]]));
}
for (i=0; i<warnings.length; i++) {
dom.badges.append(format("<li class='warning'>{0}</li>", messages[warnings[i]]));
}
// sort the actionQueue by priority
this.actionQueue.sort(function (a, b) {return b[0]-a[0];});
};
function collectHashes() {
dom.self.find('.button[data-hash]').each(function() {
hashes[$(this).attr('href')] = $(this).attr('data-hash');
});
}
function startInstall(e) {
e.preventDefault();
self.currentAction=0;
activeInstaller = $(this);
nextAction();
}
// performs the next action in the queue.
function nextAction() {
if (self.currentAction >= self.actionQueue.length) return;
// execute the next action.
var result = self.actionQueue[self.currentAction][1].call(this);
self.currentAction++;
// execute the next action if the current action returns true.
if (result === true) {
self.resumeInstall();
}
}
this.resumeInstall = function() {
// moving on.
nextAction();
};
//collects all the classes and parameters from the DOM elements.
function initFromDom() {
var b = dom.self;
self.attr = {
'addon' : b.attr('data-addon'),
'min' : b.attr('data-min'),
'max' : b.attr('data-max'),
'name' : b.attr('data-name'),
'icon' : b.attr('data-icon'),
'after' : b.attr('data-after'),
'search' : b.hasattr('data-search'),
'accept_eula' : b.hasClass('accept')
};
self.classes = {
'selfhosted' : b.hasClass('selfhosted'),
'beta' : b.hasClass('beta'),
'unreviewed' : b.hasClass('unreviewed'), // && !beta,
'persona' : b.hasClass('persona'),
'contrib' : b.hasClass('contrib'),
'search' : b.hasattr('data-search'),
'eula' : b.hasClass('eula')
};
}
// Add version and platform warnings and (optionally) popups. This is one
// big function since we merge the messaging when bad platform and version
// occur simultaneously. Returns true if a popup was added.
function versionPlatformCheck(options) {
var b = dom.self,
attr = self.attr,
classes = self.classes,
platformer = !!b.find('.platform').length,
platformSupported = !platformer || dom.buttons.filter("." + z.platform).length,
appSupported = z.appMatchesUserAgent && attr.min && attr.max,
olderBrowser, newerBrowser,
canInstall = true;
// min and max only exist if the add-on is compatible with request[APP].
if (appSupported && platformSupported) {
// The user *has* an older/newer browser.
self.tooOld = VersionCompare.compareVersions(z.browserVersion, attr.min) < 0;
self.tooNew = VersionCompare.compareVersions(z.browserVersion, attr.max) > 0;
if (self.tooOld || self.tooNew) {
canInstall = false;
}
if (self.tooOld) errors.push("tooOld");
if (self.tooNew) errors.push("tooNew");
} else {
if (!appSupported) errors.push("badApp");
if (!platformSupported) {
errors.push("badPlatform");
dom.buttons.hide().eq(0).show();
}
canInstall = false;
}
if (platformer) {
dom.self.find(format(".platform:not(.{0})", z.platform)).hide();
}
if (classes.beta) warnings.push("experimental");
if (classes.unreviewed) warnings.push("unreviewed");
if (classes.beta || classes.unreviewed) {
dom.buttons.addClass("warning");
}
if (classes.eula) {
self.actionQueue.push([1,z.eula.show]);
z.eula.acceptButton.click(_pd(self.resumeInstall));
}
if (!canInstall) {
dom.buttons.addClass("disabled");
dom.buttons.each(function() {
this.removeAttribute("href");
});
} else {
dom.buttons.click(startInstall);
}
}
//and of course, initialize the button.
this.init();
}
z.b = function() {
new Button(this);
};
jQuery.fn.installButton = function() {
return this.each(z.b);
};
})();